001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.server; 018 019import java.security.Principal; 020import java.time.Instant; 021import java.util.Map; 022import java.util.function.Consumer; 023import java.util.function.Function; 024 025import reactor.core.publisher.Mono; 026 027import org.springframework.context.ApplicationContext; 028import org.springframework.context.i18n.LocaleContext; 029import org.springframework.http.codec.multipart.Part; 030import org.springframework.http.server.reactive.ServerHttpRequest; 031import org.springframework.http.server.reactive.ServerHttpResponse; 032import org.springframework.lang.Nullable; 033import org.springframework.util.Assert; 034import org.springframework.util.MultiValueMap; 035 036/** 037 * Contract for an HTTP request-response interaction. Provides access to the HTTP 038 * request and response and also exposes additional server-side processing 039 * related properties and features such as request attributes. 040 * 041 * @author Rossen Stoyanchev 042 * @since 5.0 043 */ 044public interface ServerWebExchange { 045 046 /** 047 * Name of {@link #getAttributes() attribute} whose value can be used to 048 * correlate log messages for this exchange. Use {@link #getLogPrefix()} to 049 * obtain a consistently formatted prefix based on this attribute. 050 * @since 5.1 051 * @see #getLogPrefix() 052 */ 053 String LOG_ID_ATTRIBUTE = ServerWebExchange.class.getName() + ".LOG_ID"; 054 055 056 /** 057 * Return the current HTTP request. 058 */ 059 ServerHttpRequest getRequest(); 060 061 /** 062 * Return the current HTTP response. 063 */ 064 ServerHttpResponse getResponse(); 065 066 /** 067 * Return a mutable map of request attributes for the current exchange. 068 */ 069 Map<String, Object> getAttributes(); 070 071 /** 072 * Return the request attribute value if present. 073 * @param name the attribute name 074 * @param <T> the attribute type 075 * @return the attribute value 076 */ 077 @SuppressWarnings("unchecked") 078 @Nullable 079 default <T> T getAttribute(String name) { 080 return (T) getAttributes().get(name); 081 } 082 083 /** 084 * Return the request attribute value or if not present raise an 085 * {@link IllegalArgumentException}. 086 * @param name the attribute name 087 * @param <T> the attribute type 088 * @return the attribute value 089 */ 090 @SuppressWarnings("unchecked") 091 default <T> T getRequiredAttribute(String name) { 092 T value = getAttribute(name); 093 Assert.notNull(value, () -> "Required attribute '" + name + "' is missing"); 094 return value; 095 } 096 097 /** 098 * Return the request attribute value, or a default, fallback value. 099 * @param name the attribute name 100 * @param defaultValue a default value to return instead 101 * @param <T> the attribute type 102 * @return the attribute value 103 */ 104 @SuppressWarnings("unchecked") 105 default <T> T getAttributeOrDefault(String name, T defaultValue) { 106 return (T) getAttributes().getOrDefault(name, defaultValue); 107 } 108 109 /** 110 * Return the web session for the current request. Always guaranteed to 111 * return an instance either matching to the session id requested by the 112 * client, or with a new session id either because the client did not 113 * specify one or because the underlying session had expired. Use of this 114 * method does not automatically create a session. See {@link WebSession} 115 * for more details. 116 */ 117 Mono<WebSession> getSession(); 118 119 /** 120 * Return the authenticated user for the request, if any. 121 */ 122 <T extends Principal> Mono<T> getPrincipal(); 123 124 /** 125 * Return the form data from the body of the request if the Content-Type is 126 * {@code "application/x-www-form-urlencoded"} or an empty map otherwise. 127 * <p><strong>Note:</strong> calling this method causes the request body to 128 * be read and parsed in full and the resulting {@code MultiValueMap} is 129 * cached so that this method is safe to call more than once. 130 */ 131 Mono<MultiValueMap<String, String>> getFormData(); 132 133 /** 134 * Return the parts of a multipart request if the Content-Type is 135 * {@code "multipart/form-data"} or an empty map otherwise. 136 * <p><strong>Note:</strong> calling this method causes the request body to 137 * be read and parsed in full and the resulting {@code MultiValueMap} is 138 * cached so that this method is safe to call more than once. 139 * <p><strong>Note:</strong>the {@linkplain Part#content() contents} of each 140 * part is not cached, and can only be read once. 141 */ 142 Mono<MultiValueMap<String, Part>> getMultipartData(); 143 144 /** 145 * Return the {@link LocaleContext} using the configured 146 * {@link org.springframework.web.server.i18n.LocaleContextResolver}. 147 */ 148 LocaleContext getLocaleContext(); 149 150 /** 151 * Return the {@link ApplicationContext} associated with the web application, 152 * if it was initialized with one via 153 * {@link org.springframework.web.server.adapter.WebHttpHandlerBuilder#applicationContext(ApplicationContext)}. 154 * @since 5.0.3 155 * @see org.springframework.web.server.adapter.WebHttpHandlerBuilder#applicationContext(ApplicationContext) 156 */ 157 @Nullable 158 ApplicationContext getApplicationContext(); 159 160 /** 161 * Returns {@code true} if the one of the {@code checkNotModified} methods 162 * in this contract were used and they returned true. 163 */ 164 boolean isNotModified(); 165 166 /** 167 * An overloaded variant of {@link #checkNotModified(String, Instant)} with 168 * a last-modified timestamp only. 169 * @param lastModified the last-modified time 170 * @return whether the request qualifies as not modified 171 */ 172 boolean checkNotModified(Instant lastModified); 173 174 /** 175 * An overloaded variant of {@link #checkNotModified(String, Instant)} with 176 * an {@code ETag} (entity tag) value only. 177 * @param etag the entity tag for the underlying resource. 178 * @return true if the request does not require further processing. 179 */ 180 boolean checkNotModified(String etag); 181 182 /** 183 * Check whether the requested resource has been modified given the supplied 184 * {@code ETag} (entity tag) and last-modified timestamp as determined by 185 * the application. Also transparently prepares the response, setting HTTP 186 * status, and adding "ETag" and "Last-Modified" headers when applicable. 187 * This method works with conditional GET/HEAD requests as well as with 188 * conditional POST/PUT/DELETE requests. 189 * <p><strong>Note:</strong> The HTTP specification recommends setting both 190 * ETag and Last-Modified values, but you can also use 191 * {@code #checkNotModified(String)} or 192 * {@link #checkNotModified(Instant)}. 193 * @param etag the entity tag that the application determined for the 194 * underlying resource. This parameter will be padded with quotes (") 195 * if necessary. 196 * @param lastModified the last-modified timestamp that the application 197 * determined for the underlying resource 198 * @return true if the request does not require further processing. 199 */ 200 boolean checkNotModified(@Nullable String etag, Instant lastModified); 201 202 /** 203 * Transform the given url according to the registered transformation function(s). 204 * By default, this method returns the given {@code url}, though additional 205 * transformation functions can by registered with {@link #addUrlTransformer} 206 * @param url the URL to transform 207 * @return the transformed URL 208 */ 209 String transformUrl(String url); 210 211 /** 212 * Register an additional URL transformation function for use with {@link #transformUrl}. 213 * The given function can be used to insert an id for authentication, a nonce for CSRF 214 * protection, etc. 215 * <p>Note that the given function is applied after any previously registered functions. 216 * @param transformer a URL transformation function to add 217 */ 218 void addUrlTransformer(Function<String, String> transformer); 219 220 /** 221 * Return a log message prefix to use to correlate messages for this exchange. 222 * The prefix is based on the value of the attribute {@link #LOG_ID_ATTRIBUTE} 223 * along with some extra formatting so that the prefix can be conveniently 224 * prepended with no further formatting no separators required. 225 * @return the log message prefix or an empty String if the 226 * {@link #LOG_ID_ATTRIBUTE} is not set. 227 * @since 5.1 228 */ 229 String getLogPrefix(); 230 231 /** 232 * Return a builder to mutate properties of this exchange by wrapping it 233 * with {@link ServerWebExchangeDecorator} and returning either mutated 234 * values or delegating back to this instance. 235 */ 236 default Builder mutate() { 237 return new DefaultServerWebExchangeBuilder(this); 238 } 239 240 241 /** 242 * Builder for mutating an existing {@link ServerWebExchange}. 243 * Removes the need 244 */ 245 interface Builder { 246 247 /** 248 * Configure a consumer to modify the current request using a builder. 249 * <p>Effectively this: 250 * <pre> 251 * exchange.mutate().request(builder-> builder.method(HttpMethod.PUT)); 252 * 253 * // vs... 254 * 255 * ServerHttpRequest request = exchange.getRequest().mutate() 256 * .method(HttpMethod.PUT) 257 * .build(); 258 * 259 * exchange.mutate().request(request); 260 * </pre> 261 * @see ServerHttpRequest#mutate() 262 */ 263 Builder request(Consumer<ServerHttpRequest.Builder> requestBuilderConsumer); 264 265 /** 266 * Set the request to use especially when there is a need to override 267 * {@link ServerHttpRequest} methods. To simply mutate request properties 268 * see {@link #request(Consumer)} instead. 269 * @see org.springframework.http.server.reactive.ServerHttpRequestDecorator 270 */ 271 Builder request(ServerHttpRequest request); 272 273 /** 274 * Set the response to use. 275 * @see org.springframework.http.server.reactive.ServerHttpResponseDecorator 276 */ 277 Builder response(ServerHttpResponse response); 278 279 /** 280 * Set the {@code Mono<Principal>} to return for this exchange. 281 */ 282 Builder principal(Mono<Principal> principalMono); 283 284 /** 285 * Build a {@link ServerWebExchange} decorator with the mutated properties. 286 */ 287 ServerWebExchange build(); 288 } 289 290}