001/* 002 * Copyright 2002-2020 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.reactive.function.server; 018 019import java.net.InetSocketAddress; 020import java.net.URI; 021import java.nio.charset.Charset; 022import java.security.Principal; 023import java.time.Instant; 024import java.util.List; 025import java.util.Locale; 026import java.util.Map; 027import java.util.Optional; 028import java.util.OptionalLong; 029import java.util.function.Consumer; 030 031import reactor.core.publisher.Flux; 032import reactor.core.publisher.Mono; 033 034import org.springframework.core.ParameterizedTypeReference; 035import org.springframework.core.io.buffer.DataBuffer; 036import org.springframework.http.HttpCookie; 037import org.springframework.http.HttpHeaders; 038import org.springframework.http.HttpMethod; 039import org.springframework.http.HttpRange; 040import org.springframework.http.MediaType; 041import org.springframework.http.codec.HttpMessageReader; 042import org.springframework.http.codec.json.Jackson2CodecSupport; 043import org.springframework.http.codec.multipart.Part; 044import org.springframework.http.server.PathContainer; 045import org.springframework.http.server.reactive.ServerHttpRequest; 046import org.springframework.lang.Nullable; 047import org.springframework.util.Assert; 048import org.springframework.util.CollectionUtils; 049import org.springframework.util.MultiValueMap; 050import org.springframework.web.reactive.function.BodyExtractor; 051import org.springframework.web.server.ServerWebExchange; 052import org.springframework.web.server.WebSession; 053import org.springframework.web.util.UriBuilder; 054 055/** 056 * Represents a server-side HTTP request, as handled by a {@code HandlerFunction}. 057 * 058 * <p>Access to headers and body is offered by {@link Headers} and 059 * {@link #body(BodyExtractor)}, respectively. 060 * 061 * @author Arjen Poutsma 062 * @author Sebastien Deleuze 063 * @since 5.0 064 */ 065public interface ServerRequest { 066 067 /** 068 * Get the HTTP method. 069 * @return the HTTP method as an HttpMethod enum value, or {@code null} 070 * if not resolvable (e.g. in case of a non-standard HTTP method) 071 */ 072 @Nullable 073 default HttpMethod method() { 074 return HttpMethod.resolve(methodName()); 075 } 076 077 /** 078 * Get the name of the HTTP method. 079 * @return the HTTP method as a String 080 */ 081 String methodName(); 082 083 /** 084 * Get the request URI. 085 */ 086 URI uri(); 087 088 /** 089 * Get a {@code UriBuilderComponents} from the URI associated with this 090 * {@code ServerRequest}. 091 * <p><strong>Note:</strong> as of 5.1 this method ignores {@code "Forwarded"} 092 * and {@code "X-Forwarded-*"} headers that specify the 093 * client-originated address. Consider using the {@code ForwardedHeaderFilter} 094 * to extract and use, or to discard such headers. 095 * @return a URI builder 096 */ 097 UriBuilder uriBuilder(); 098 099 /** 100 * Get the request path. 101 */ 102 default String path() { 103 return uri().getRawPath(); 104 } 105 106 /** 107 * Get the request path as a {@code PathContainer}. 108 */ 109 default PathContainer pathContainer() { 110 return PathContainer.parsePath(path()); 111 } 112 113 /** 114 * Get the headers of this request. 115 */ 116 Headers headers(); 117 118 /** 119 * Get the cookies of this request. 120 */ 121 MultiValueMap<String, HttpCookie> cookies(); 122 123 /** 124 * Get the remote address to which this request is connected, if available. 125 * @since 5.1 126 */ 127 Optional<InetSocketAddress> remoteAddress(); 128 129 /** 130 * Get the local address to which this request is connected, if available. 131 * @since 5.2.3 132 */ 133 Optional<InetSocketAddress> localAddress(); 134 135 /** 136 * Get the readers used to convert the body of this request. 137 * @since 5.1 138 */ 139 List<HttpMessageReader<?>> messageReaders(); 140 141 /** 142 * Extract the body with the given {@code BodyExtractor}. 143 * @param extractor the {@code BodyExtractor} that reads from the request 144 * @param <T> the type of the body returned 145 * @return the extracted body 146 * @see #body(BodyExtractor, Map) 147 */ 148 <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor); 149 150 /** 151 * Extract the body with the given {@code BodyExtractor} and hints. 152 * @param extractor the {@code BodyExtractor} that reads from the request 153 * @param hints the map of hints like {@link Jackson2CodecSupport#JSON_VIEW_HINT} 154 * to use to customize body extraction 155 * @param <T> the type of the body returned 156 * @return the extracted body 157 */ 158 <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor, Map<String, Object> hints); 159 160 /** 161 * Extract the body to a {@code Mono}. 162 * @param elementClass the class of element in the {@code Mono} 163 * @param <T> the element type 164 * @return the body as a mono 165 */ 166 <T> Mono<T> bodyToMono(Class<? extends T> elementClass); 167 168 /** 169 * Extract the body to a {@code Mono}. 170 * @param typeReference a type reference describing the expected response request type 171 * @param <T> the element type 172 * @return a mono containing the body of the given type {@code T} 173 */ 174 <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> typeReference); 175 176 /** 177 * Extract the body to a {@code Flux}. 178 * @param elementClass the class of element in the {@code Flux} 179 * @param <T> the element type 180 * @return the body as a flux 181 */ 182 <T> Flux<T> bodyToFlux(Class<? extends T> elementClass); 183 184 /** 185 * Extract the body to a {@code Flux}. 186 * @param typeReference a type reference describing the expected request body type 187 * @param <T> the element type 188 * @return a flux containing the body of the given type {@code T} 189 */ 190 <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> typeReference); 191 192 /** 193 * Get the request attribute value if present. 194 * @param name the attribute name 195 * @return the attribute value 196 */ 197 default Optional<Object> attribute(String name) { 198 return Optional.ofNullable(attributes().get(name)); 199 } 200 201 /** 202 * Get a mutable map of request attributes. 203 * @return the request attributes 204 */ 205 Map<String, Object> attributes(); 206 207 /** 208 * Get the first query parameter with the given name, if present. 209 * @param name the parameter name 210 * @return the parameter value 211 */ 212 default Optional<String> queryParam(String name) { 213 List<String> queryParamValues = queryParams().get(name); 214 if (CollectionUtils.isEmpty(queryParamValues)) { 215 return Optional.empty(); 216 } 217 else { 218 String value = queryParamValues.get(0); 219 if (value == null) { 220 value = ""; 221 } 222 return Optional.of(value); 223 } 224 } 225 226 /** 227 * Get all query parameters for this request. 228 */ 229 MultiValueMap<String, String> queryParams(); 230 231 /** 232 * Get the path variable with the given name, if present. 233 * @param name the variable name 234 * @return the variable value 235 * @throws IllegalArgumentException if there is no path variable with the given name 236 */ 237 default String pathVariable(String name) { 238 Map<String, String> pathVariables = pathVariables(); 239 if (pathVariables.containsKey(name)) { 240 return pathVariables().get(name); 241 } 242 else { 243 throw new IllegalArgumentException("No path variable with name \"" + name + "\" available"); 244 } 245 } 246 247 /** 248 * Get all path variables for this request. 249 */ 250 Map<String, String> pathVariables(); 251 252 /** 253 * Get the web session for this request. 254 * <p>Always guaranteed to return an instance either matching the session id 255 * requested by the client, or with a new session id either because the client 256 * did not specify one or because the underlying session had expired. 257 * <p>Use of this method does not automatically create a session. 258 */ 259 Mono<WebSession> session(); 260 261 /** 262 * Get the authenticated user for the request, if any. 263 */ 264 Mono<? extends Principal> principal(); 265 266 /** 267 * Get the form data from the body of the request if the Content-Type is 268 * {@code "application/x-www-form-urlencoded"} or an empty map otherwise. 269 * <p><strong>Note:</strong> calling this method causes the request body to 270 * be read and parsed in full, and the resulting {@code MultiValueMap} is 271 * cached so that this method is safe to call more than once. 272 */ 273 Mono<MultiValueMap<String, String>> formData(); 274 275 /** 276 * Get the parts of a multipart request if the Content-Type is 277 * {@code "multipart/form-data"} or an empty map otherwise. 278 * <p><strong>Note:</strong> calling this method causes the request body to 279 * be read and parsed in full, and the resulting {@code MultiValueMap} is 280 * cached so that this method is safe to call more than once. 281 */ 282 Mono<MultiValueMap<String, Part>> multipartData(); 283 284 /** 285 * Get the web exchange that this request is based on. 286 * <p>Note: Manipulating the exchange directly (instead of using the methods provided on 287 * {@code ServerRequest} and {@code ServerResponse}) can lead to irregular results. 288 * @since 5.1 289 */ 290 ServerWebExchange exchange(); 291 292 /** 293 * Check whether the requested resource has been modified given the 294 * supplied last-modified timestamp (as determined by the application). 295 * <p>If not modified, this method returns a response with corresponding 296 * status code and headers, otherwise an empty result. 297 * <p>Typical usage: 298 * <pre class="code"> 299 * public Mono<ServerResponse> myHandleMethod(ServerRequest request) { 300 * Instant lastModified = // application-specific calculation 301 * return request.checkNotModified(lastModified) 302 * .switchIfEmpty(Mono.defer(() -> { 303 * // further request processing, actually building content 304 * return ServerResponse.ok().body(...); 305 * })); 306 * }</pre> 307 * <p>This method works with conditional GET/HEAD requests, but 308 * also with conditional POST/PUT/DELETE requests. 309 * <p><strong>Note:</strong> you can use either 310 * this {@code #checkNotModified(Instant)} method; or 311 * {@link #checkNotModified(String)}. If you want enforce both 312 * a strong entity tag and a Last-Modified value, 313 * as recommended by the HTTP specification, 314 * then you should use {@link #checkNotModified(Instant, String)}. 315 * @param lastModified the last-modified timestamp that the 316 * application determined for the underlying resource 317 * @return a corresponding response if the request qualifies as not 318 * modified, or an empty result otherwise 319 * @since 5.2.5 320 */ 321 default Mono<ServerResponse> checkNotModified(Instant lastModified) { 322 Assert.notNull(lastModified, "LastModified must not be null"); 323 return DefaultServerRequest.checkNotModified(exchange(), lastModified, null); 324 } 325 326 /** 327 * Check whether the requested resource has been modified given the 328 * supplied {@code ETag} (entity tag), as determined by the application. 329 * <p>If not modified, this method returns a response with corresponding 330 * status code and headers, otherwise an empty result. 331 * <p>Typical usage: 332 * <pre class="code"> 333 * public Mono<ServerResponse> myHandleMethod(ServerRequest request) { 334 * String eTag = // application-specific calculation 335 * return request.checkNotModified(eTag) 336 * .switchIfEmpty(Mono.defer(() -> { 337 * // further request processing, actually building content 338 * return ServerResponse.ok().body(...); 339 * })); 340 * }</pre> 341 * <p>This method works with conditional GET/HEAD requests, but 342 * also with conditional POST/PUT/DELETE requests. 343 * <p><strong>Note:</strong> you can use either 344 * this {@link #checkNotModified(Instant)} method; or 345 * {@code #checkNotModified(String)}. If you want enforce both 346 * a strong entity tag and a Last-Modified value, 347 * as recommended by the HTTP specification, 348 * then you should use {@link #checkNotModified(Instant, String)}. 349 * @param etag the entity tag that the application determined 350 * for the underlying resource. This parameter will be padded 351 * with quotes (") if necessary. 352 * @return a corresponding response if the request qualifies as not 353 * modified, or an empty result otherwise 354 * @since 5.2.5 355 */ 356 default Mono<ServerResponse> checkNotModified(String etag) { 357 Assert.notNull(etag, "Etag must not be null"); 358 return DefaultServerRequest.checkNotModified(exchange(), null, etag); 359 } 360 361 /** 362 * Check whether the requested resource has been modified given the 363 * supplied {@code ETag} (entity tag) and last-modified timestamp, 364 * as determined by the application. 365 * <p>If not modified, this method returns a response with corresponding 366 * status code and headers, otherwise an empty result. 367 * <p>Typical usage: 368 * <pre class="code"> 369 * public Mono<ServerResponse> myHandleMethod(ServerRequest request) { 370 * Instant lastModified = // application-specific calculation 371 * String eTag = // application-specific calculation 372 * return request.checkNotModified(lastModified, eTag) 373 * .switchIfEmpty(Mono.defer(() -> { 374 * // further request processing, actually building content 375 * return ServerResponse.ok().body(...); 376 * })); 377 * }</pre> 378 * <p>This method works with conditional GET/HEAD requests, but 379 * also with conditional POST/PUT/DELETE requests. 380 * @param lastModified the last-modified timestamp that the 381 * application determined for the underlying resource 382 * @param etag the entity tag that the application determined 383 * for the underlying resource. This parameter will be padded 384 * with quotes (") if necessary. 385 * @return a corresponding response if the request qualifies as not 386 * modified, or an empty result otherwise. 387 * @since 5.2.5 388 */ 389 default Mono<ServerResponse> checkNotModified(Instant lastModified, String etag) { 390 Assert.notNull(lastModified, "LastModified must not be null"); 391 Assert.notNull(etag, "Etag must not be null"); 392 return DefaultServerRequest.checkNotModified(exchange(), lastModified, etag); 393 } 394 395 // Static builder methods 396 397 /** 398 * Create a new {@code ServerRequest} based on the given {@code ServerWebExchange} and 399 * message readers. 400 * @param exchange the exchange 401 * @param messageReaders the message readers 402 * @return the created {@code ServerRequest} 403 */ 404 static ServerRequest create(ServerWebExchange exchange, List<HttpMessageReader<?>> messageReaders) { 405 return new DefaultServerRequest(exchange, messageReaders); 406 } 407 408 /** 409 * Create a builder with the {@linkplain HttpMessageReader message readers}, 410 * method name, URI, headers, cookies, and attributes of the given request. 411 * @param other the request to copy the message readers, method name, URI, 412 * headers, and attributes from 413 * @return the created builder 414 * @since 5.1 415 */ 416 static Builder from(ServerRequest other) { 417 return new DefaultServerRequestBuilder(other); 418 } 419 420 421 /** 422 * Represents the headers of the HTTP request. 423 * @see ServerRequest#headers() 424 */ 425 interface Headers { 426 427 /** 428 * Get the list of acceptable media types, as specified by the {@code Accept} 429 * header. 430 * <p>Returns an empty list if the acceptable media types are unspecified. 431 */ 432 List<MediaType> accept(); 433 434 /** 435 * Get the list of acceptable charsets, as specified by the 436 * {@code Accept-Charset} header. 437 */ 438 List<Charset> acceptCharset(); 439 440 /** 441 * Get the list of acceptable languages, as specified by the 442 * {@code Accept-Language} header. 443 */ 444 List<Locale.LanguageRange> acceptLanguage(); 445 446 /** 447 * Get the length of the body in bytes, as specified by the 448 * {@code Content-Length} header. 449 */ 450 OptionalLong contentLength(); 451 452 /** 453 * Get the media type of the body, as specified by the 454 * {@code Content-Type} header. 455 */ 456 Optional<MediaType> contentType(); 457 458 /** 459 * Get the value of the {@code Host} header, if available. 460 * <p>If the header value does not contain a port, the 461 * {@linkplain InetSocketAddress#getPort() port} in the returned address will 462 * be {@code 0}. 463 */ 464 @Nullable 465 InetSocketAddress host(); 466 467 /** 468 * Get the value of the {@code Range} header. 469 * <p>Returns an empty list when the range is unknown. 470 */ 471 List<HttpRange> range(); 472 473 /** 474 * Get the header value(s), if any, for the header with the given name. 475 * <p>Returns an empty list if no header values are found. 476 * @param headerName the header name 477 */ 478 List<String> header(String headerName); 479 480 /** 481 * Get the first header value, if any, for the header with the given name. 482 * <p>Returns {@code null} if no header values are found. 483 * @param headerName the header name 484 * @since 5.2.5 485 */ 486 @Nullable 487 default String firstHeader(String headerName) { 488 List<String> list = header(headerName); 489 return list.isEmpty() ? null : list.get(0); 490 } 491 492 /** 493 * Get the headers as an instance of {@link HttpHeaders}. 494 */ 495 HttpHeaders asHttpHeaders(); 496 } 497 498 499 /** 500 * Defines a builder for a request. 501 * @since 5.1 502 */ 503 interface Builder { 504 505 /** 506 * Set the method of the request. 507 * @param method the new method 508 * @return this builder 509 */ 510 Builder method(HttpMethod method); 511 512 /** 513 * Set the URI of the request. 514 * @param uri the new URI 515 * @return this builder 516 */ 517 Builder uri(URI uri); 518 519 /** 520 * Add the given header value(s) under the given name. 521 * @param headerName the header name 522 * @param headerValues the header value(s) 523 * @return this builder 524 * @see HttpHeaders#add(String, String) 525 */ 526 Builder header(String headerName, String... headerValues); 527 528 /** 529 * Manipulate this request's headers with the given consumer. 530 * <p>The headers provided to the consumer are "live", so that the consumer can be used to 531 * {@linkplain HttpHeaders#set(String, String) overwrite} existing header values, 532 * {@linkplain HttpHeaders#remove(Object) remove} values, or use any of the other 533 * {@link HttpHeaders} methods. 534 * @param headersConsumer a function that consumes the {@code HttpHeaders} 535 * @return this builder 536 */ 537 Builder headers(Consumer<HttpHeaders> headersConsumer); 538 539 /** 540 * Add a cookie with the given name and value(s). 541 * @param name the cookie name 542 * @param values the cookie value(s) 543 * @return this builder 544 */ 545 Builder cookie(String name, String... values); 546 547 /** 548 * Manipulate this request's cookies with the given consumer. 549 * <p>The map provided to the consumer is "live", so that the consumer can be used to 550 * {@linkplain MultiValueMap#set(Object, Object) overwrite} existing cookies, 551 * {@linkplain MultiValueMap#remove(Object) remove} cookies, or use any of the other 552 * {@link MultiValueMap} methods. 553 * @param cookiesConsumer a function that consumes the cookies map 554 * @return this builder 555 */ 556 Builder cookies(Consumer<MultiValueMap<String, HttpCookie>> cookiesConsumer); 557 558 /** 559 * Set the body of the request. 560 * <p>Calling this methods will 561 * {@linkplain org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer) release} 562 * the existing body of the builder. 563 * @param body the new body 564 * @return this builder 565 */ 566 Builder body(Flux<DataBuffer> body); 567 568 /** 569 * Set the body of the request to the UTF-8 encoded bytes of the given string. 570 * <p>Calling this methods will 571 * {@linkplain org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer) release} 572 * the existing body of the builder. 573 * @param body the new body 574 * @return this builder 575 */ 576 Builder body(String body); 577 578 /** 579 * Add an attribute with the given name and value. 580 * @param name the attribute name 581 * @param value the attribute value 582 * @return this builder 583 */ 584 Builder attribute(String name, Object value); 585 586 /** 587 * Manipulate this request's attributes with the given consumer. 588 * <p>The map provided to the consumer is "live", so that the consumer can be used 589 * to {@linkplain Map#put(Object, Object) overwrite} existing attributes, 590 * {@linkplain Map#remove(Object) remove} attributes, or use any of the other 591 * {@link Map} methods. 592 * @param attributesConsumer a function that consumes the attributes map 593 * @return this builder 594 */ 595 Builder attributes(Consumer<Map<String, Object>> attributesConsumer); 596 597 /** 598 * Build the request. 599 * @return the built request 600 */ 601 ServerRequest build(); 602 } 603 604}