WebFlux knowledge structure

Written in the beginning

Due to my busy curriculum, project development, postgraduate entrance examination and other matters, it is slow to update the article. If you think my article is right for you, please send me a message and I will update it in time.

Index

  1. In the meaning of @ “Validated”, data range and type verification shall be carried out when Bean equipment is carried out;

Specifically, some Bean definitions define not only the data type but also the data scope; To construct such a Bean, you need to evaluate the incoming data; And Spring integrates this judgment process, the @Validated annotation

Need basis

Spring Framework

JDK 8.0+ functional programming

Netty

Reactor responsive programming

Spring WebFlux

Reactive Core

HttpHandler

A slightly lower-level protocol for HTTP requests; Tomcat, Jetty, Netty, Undertow, Servlet3.1 and other servers are standardized. It aims to provide a more abstract and unified interface for different Web servers.

Netty

HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create().host(host).port(port).handle(adapter).bind().block();
Copy the code

Undertow

HttpHandler handler = ...
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();
Copy the code

Tomcat

HttpHandler handler = ...
Servlet servlet = new TomcatHttpHandlerAdapter(handler);

Tomcat server = new Tomcat();
File base = new File(System.getProperty("java.io.tmpdir"));
Context rootContext = server.addContext("", base.getAbsolutePath());
Tomcat.addServlet(rootContext, "main", servlet);
rootContext.addServletMappingDecoded("/"."main");
server.setHost(host);
server.setPort(port);
server.start();
Copy the code

Jetty

HttpHandler handler = ...
Servlet servlet = new JettyHttpHandlerAdapter(handler);

Server server = new Server();
ServletContextHandler contextHandler = new ServletContextHandler(server, "");
contextHandler.addServlet(new ServletHolder(servlet), "/");
contextHandler.start();

ServerConnector connector = new ServerConnector(server);
connector.setHost(host);
connector.setPort(port);
server.addConnector(connector);
server.start();
Copy the code

Servlet3.1

WebHandler API

Based on HttpHandler, and provide a slightly more advanced and more commonly used Web API to make code writing easier; And build a more concrete programming model. A “chain of responsibilities” process consisting of several parts (the same idea as Netty’s chain process).

Special Bean Types

1. WebExceptionHandler

Provides the handling of Filter exceptions and WebHandler exceptions in the Filter chain, the number of 0-n.

2. WebFilter

Provides “intercepting” logic processing, quantity 0-N. Intercepts before or after the rest of the Filter chain and WebHandler (for Filter).

3. WebHandler

The processor, which is also the main business processing part (the Controller in Spring MVC).

4. WebSessionManager

A manager for WebSession.

5. ServerCodecConfigurer

Used to access HttpMessageReader instances to parse form data and file data; The data is then passed through the ServerWebExchange method.

6. LocaleContextResolver

LocaleContext parser.

7. ForwardedHeaderTransformer

Processing HTTP headers, including extracting and removing some header key-value pairs, or removing only.

Form Data

ServerWebExchange provides the Mono<MultiValueMap<String, String>> getFormData() method; This method is used to access form data.

Multipart Data

ServerWebExchange provides the Mono<MultiValueMap<String, Part>> getMultipartData() method; This method is used to access Multipart data.

DefaultServerWebExchange parses the contents of “multiPart /form-data” into MultiValueMap using the configured HttpMessageReader<MultiValueMap<String, Part>>.

Currently, Synchronoss NIO Multipart is the only supported third-party library for non-blocking resolution of Multipart requests.

To parse multipart data into streams, Flux can be used as the return value of HttpMessageReader instead of HttpMessageReader<MultiValueMap<String, Part>>.

For example, in an annotation Controller, using @requestPart means that the entire Multipart data needs to be parsed for map-like access to each individual part by name.

In contrast, you can use @requestbody to parse the RequestBody into Flux without having to integrate it into MultiValueMap.

Forwarded Headers

If the request goes through a proxy server (such as Nginx), the host address, port, and request method may be changed. At this point, it can be a challenge for the client to correctly find information such as the host address.

ForwardedHeaderTransformer is a component class that provides based on the request to modify the host, port, and the method of function; These request headers are then removed, and the class can be declared in bean form.

Can be done by setting ForwardedHeaderTransformer removeOnly = true only remove unsafe head without access to some effect.

Filters

In the WebHandler API, you can use WebFilter to implement ‘intercepting’ logic; A WebFilter can intercept data flows into or from other Webfilters or WebHandlers.

When using WebFlux Config, you will find that registering a WebFilter is as easy as declaring it as a Spring Bean; Optionally, you can use the @Order annotation or implement the Ordered interface to sort webfilters.

CORS(Cross-domain Access)

Spring WebFlux provides fine-grained support for CORS through annotations of controller components. However, when you use Spring Security, we recommend (not me, Spring officials) using the built-in CorsFilter class, which must precede all Spring Security filters.

Exceptions (abnormal)

In the WebHandler API, you can use WebExceptionHandler to handle exceptions from WebFilter or WebHandler.

When using WebFlux Config, registering a WebExceptionHandler is as easy as declaring a Spring Bean.

The Order can also be set using @Order or implementing the Ordered interface.

There are two available WebExceptionHandler implementation, respectively: ResponseStatusExceptionHandler: By setting the HTTP response Code to handle ResponseStatusExceptionHandler type of exception. WebFluxResponseStatusExceptionHandler: ResponseStatusExceptionHandler derived classes, can be in any exception decided @ ResponseStatus annotation HTTP status code.

Codecs(Codecs)

The Spring-Web and Spring-Core modules provide support for deserialization from byte data to high-level objects and high-level object to byte serialization through non-blocking IO with back-pressure reactive flow.

The above support is described below:

Encoder and Decoder are low-level protocols used to encode/decode detached HTTP content.

HttpMessageReader and HttpMessageWriter are codecs used to encode/decode the content of HTTP messages.

An encoder can be encapsulated by EncoderHttpMessageWriter to make it suitable for Web applications; Similarly, decoders can be wrapped by DecoderHttpMessageReader.

DataBuffer abstracts and encapsulates byte buffers for different servers (such as Netty’s ByteBuf, java.nio.byteBuffer); This is also the data dependency (or data source) on which all codecs work.

The Spring-core module provides encoder and decoder implementations for Byte [], ByteBuffer, DataBuffer, Resource, and String.

For form data, binary/file content, events sent by the server, etc. The Spring-Web module provides codecs including Jackson JSON, Jackson Smile, JAXB2, Protocol Buffers and a Web-only HTTP message reader/writer implementation.

ClientCodecConfigurer and ServerCodecConfigurer are commonly used to configure and customize codecs within Web applications.

Jackson JSON(Jackson JSON)

Jackson2Decoder working principle:

  1. Jackson is an asynchronous, non-blocking parser that aggregates a stream of byte blocks into each block of TokenBuffer, where each block represents a JSON object
  2. Each TokenBuffer is passed to Jackson’s ObjectMapper to create a higher-level object
  3. When decoding a single-valued Publisher (such as Mono), a TokenBuffer is generated
  4. When decoding a multi-valued publisher(such as Flux), once a complete object receives enough bytes, each TokenBuffer is passed to ObjectMapper, and the input objects can be JSON arrays, If the content-type is “application/stream+ JSON “, it can also be a row split JSON

Jackson2Decoder Purpose: To decode byte streams to JSON and convert them to Object using Jackson 2.9.

Jackson2Encoder Working principle:

  1. For single-value Publisher, simply serialize through ObjectMapper
  2. For multi-value publishers decorated with “application/json”, the default is to use flux.collecttolist () to aggregate the values and serialize the collection
  3. For multi-value Publisher modified by streaming media types (e.g., Application /stream+ JSON, Application/Stream + X-jackson-smile), encode, write, and refresh each value using a JSON format based on line separators
  4. For SSE, Jackson2Encoder is called for each event, and the result of execution is actively flushed to ensure that there is no delay in transmission

Jackson2Encoder and Jackson2Decoder do not support String and String encoding. If you need to get JSON arrays from Flux, you should use flux.collecttolist () and encode Mono<List>

Form Data

FormHttpMessageReader and FormHttpMessageWriter support coding/decoding of “Application/X-www-form-urlencoded” content

On the server side, where you often need to access form data from multiple locations, ServerWebExchange provides a dedicated getFormData() method that parses the content through FormHttpMessageReader and then caches the results for multiple accesses

Once getFormData() is called, the native data in the request body cannot be accessed again. For this reason, the application should use ServerWebExchange to access cached data instead of the native data P.S. In fact, I think this design is probably due to Netty’s reference counting

Multipart(file data/binary data)

MultipartHttpMessageReader and MultipartHttpMessageWriter provides for “multipart/form – data” type of data encoding/decoding

In fact, another HttpMessageReader MultipartHttpMessageReader through agent to do the actual data parsing (parsed into the data Flux), then simply put the result set into a MultiValueMap

Currently, Synchronoss NIO Multipart is used to do the actual parsing

On a server that often needs to access multipart data from multiple locations, ServerWebExchange provides special getMultipartData () method by MultipartHttpMessageReader to parse the contents and results are cached for multiple access (similar to the form data)

Once getMultipartData() is called, the native data in the request body cannot be accessed again. Therefore, applications should consistently use getMultipartData() to perform multiple, map-based accesses to parts. Or use SynchronossPartHttpMessageReader for only once for Flux access data will be destroyed after p.. The reason for this design is the same as form data access

Limits (limit)

Decoder and HttpMessageReader implementations that aggregate some or all of the input stream data into buffers can set the maximum size of bytes a memory can contain to avoid memory bursting

Buffer processing occurs in some cases, such as aggregating incoming data into an object (HTTP requests are sent segment by segment, so aggregating); Buffering can also occur when an input stream is split

To set the maximum buffer size, you can check whether the provided Decoder or HttpMessageReader already has the maxInMemorySize attribute. In WebFlux, there is a separate place to set this property for all codecs; On the client side, you can set this property via webClient. Builder

For Multipart data, this restriction applies only to non-file parts; For the file part, it determines the threshold at which the file can be written to the disk; For portions of a file written to the hard disk, there is a maxDiskUsagePerPart property that limits the amount of hard disk space for each portion of the file

Also, there is a maxParts attribute that limits the total number of file parts in a Multipart request. To configure these three properties in WebFlux, Need to provide a pre-configured to ServerCodecConfigurer MultipartHttpMessageReader instance

Streaming (flow)

When streaming HTTP responses, heartbeat detection mechanisms should be added and the connection should be disconnected in a timely manner

DataBuffer(Data buffering component)

Byte buffers for various servers are encapsulated

For Netty reference counting, WebFlux generally does not care about this unless it directly consumes or produces data rather than relying on codecs to convert it to high-level objects. Or build your own codec (which can also cause memory leaks)

Logging (log)

DispatcherHandler

A central Handler, the DispatcherHandler, is responsible for forwarding requests. The actual request processing is done by configured proxy components. This mode of working is flexible and supports a wide variety of workflows

The DispatcherHandler automatically discovers the proxy component it needs through the Spring configuration. It is also designed as a Spring Bean and provides access to its context by implementing the ApplicationContextAware interface

A Spring configuration for WebFlux typically contains:

  1. DispatcherHandler with Bean name “webHandler”
  2. WebFilter and WebExceptionHandler Beans
  3. A special Bean for the DispatcherHandler
  4. other

The configuration is usually passed to WebHttpHandlerBuilder to build the processing chain

Such as:

ApplicationContext context = … HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build();

Special Bean Types

The DispatcherHandler delegates the request to a special Bean to complete the request processing and return the appropriate response.

HandlerMapping

To map a request to a Handler, the mapping is based on some specification, the details of which are determined by the implementation of HandlerMapping – annotation-based controller, simple URL schema mapping, and others

The main HandlerMapping implementation class is used to @ RequestMapping RequestMappingHandlerMapping annotation methods, for RouterFunctionMapping endpoint routing function, And explicit registration of URI path patterns and SimpleUrlHandlerMapping for WebHandler instances

HandlerAdapter

Help DispatcherHandler call a mapped Handler for a request without knowing how the Handler is actually called

For example, calling an annotated controller requires parsing the annotations first, which saves the DispatcherHandler from the tedious details

HandlerResultHandler

Process the result of the call from the processor and finally determine the reply response.

WebFlux Config(WebFlux configuration)

WebFlux Config is a good way to do this

Processing (Processing)

The DispatcherHandler process the request workflow:

  1. Each HandlerMapping is woken up to find a matching handler, and the first one that matches will be the handler for the request
  2. Once a handler is found, it is executed by an appropriate HandlerAdapter, which encapsulates the execution result (return value) as a HandlerResult
  3. The HandlerResult will be passed to an appropriate HandlerResultHandler to render the view or written directly to the Response to complete the execution

Result Handling

The result of the Handler call passes through the HandlerAdapter and is encapsulated into a HandlerResult with some text attached, which is then passed to the first HandlerResultHandler that can handle it for final processing

Here are some default implementations of HandlerResultHandler: ResponseEntityResultHandler ServerResponseResultHandler ResponseBodyResultHandler ViewResolutionResultHandler

ResponseEntityResultHandler

ResponseEntity, usually from the @Controller instance.

ServerResponseResultHandler

ServerResponse, usually from a functional endpoint

ResponseBodyResultHandler

Return value from the @responseBody method or the @RestController class

ViewResolutionResultHandler

CharSequence, View, Model, Map, Rendering, and any other Object are considered Model attributes

Exceptions (abnormal)

Specific error functions are triggered when a handler call fails or when a handler return value fails to be processed through the HandlerResultHandler

Whenever an error occurs, the error function can change the response state

You can use @ControllerAdvice to handle exceptions before selecting a handler to handle them

View Resolution

Annotated Controllers

@Controller

Either a Controller Bean can be defined using the standard SpringBean method or it can be built using the @controller annotation

The @Controller annotation provides automatic inference and path scanning methods for assembling Controller components, allowing Controller classes to be recognized as Web components

@restController is a combined annotation of @Controller and @responseBody, meaning that the return value is written directly to the Response body rather than being rendered to the view or written to the HTML template

Request Mapping

The @requestMapping annotation is often used to map controller methods; It has a variety of attributes for matching, such as by URL, by HTTP request method, request parameters, headers, or media types

This annotation can be used either at the class level to indicate that all methods of the class include the path map, or at the method level for specific request processing

@requestMapping has some sub-annotations to qualify the request: @getMapping @postMapping @putMapping @deletemapping @PatchMapping

URI Patterns(URI Matching)

The use of wildcards for URI maps? : Matches one character *: matches 0 or more characters in a path fragment **: matches 0 or more path fragments

Also, you can get the value of the URI by setting a variable in the path. For example: @GetMapping(“/owners/{ownerId}/pets/{petId}”) public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) { // … } Path variable parameters can also be used for class-level mapping

URI variables can be automatically converted to the appropriate type, otherwise a TypeMismatchException will be thrown, and simple basic types (such as int, double, Long, String, Date, etc.) can be retrieved directly

URI variables can also be explicitly named (e.g. @pathVariable (“customId”))

The syntax {*varName} matches 0 or more remaining path fragments, such as (“/resources/{*path}”)

The syntax {varName: regular expression} can be used to match regular expressions

Uris can also use the embedded placeholder ${… }, to be injected as a property at system startup

WebFlux urIs differ from SpringMVC in that they cannot use postfix expressions

Pattern Comparison

When multiple maps match a URL, they must be compared to find the best match; This is done by pathPattern.specificity_Comparator, which is responsible for finding the best match

For each matchable item, a value is computed based on the number of URI variables and placeholders. A URI variable with a value smaller than the placeholders matches with a fraction smaller, and if the fraction is the same, a long match

Consumable Media Types

Request matching can be fine-grained with the content-Type attribute of the request, for example: @PostMapping(path = “/pets”, consumes = “application/json”) public void addPet(@RequestBody Pet pet) { // … }

The Consumes attribute also supports the negative form, for example:! Text /plain means to match all non-text /plain requests

As Consumes at the class level, when the method uses Consumes, it means that it overwrites the Consumes attribute for the class

MediaType encapsulates several types

Producible Media Types

The request mapping can be fine-grained based on the Accept attribute of the request, for example: @GetMapping(path = “/pets/{petId}”, produces = “application/json”) @ResponseBody public Pet getPet(@PathVariable String petId) { // … } indicates that in addition to matching the URI, the request that accepts the return type of “application/json” should be matched

The media type can also specify a character set, and also supports reverse operations (matching only accepts requests other than this type).

Method-level Settings can also override class-level Settings

Parameters and Headers

The matching can be fine-grained according to the request parameters: 1. Match according to the existence of a parameter 2. Matches a parameter based on whether it does not exist. 3. Matches a parameter based on whether it is equal to a certain value. @GetMapping(path = “/pets/{petId}”, params = “myParam=myValue”) public void findPet(@PathVariable String petId) { // … } check if myParam is equal to myValue

The same method can be used to check if the request header matches. For example: @GetMapping(path = “/pets”, headers = “myHeader=myValue”) public void findPet(@PathVariable String petId) { // … }

HTTP HEAD, OPTIONS

Annotations Custom Annotations

Spring allows you to combine annotations to implement request mapping. The goal is to get a more granular child annotation of @requestMapping.

If you want to get a more granular logical match. Inherit the RequestMappingHandlerMapping and overwrite getCustomMethodCondition () method. This allows you to check custom attributes and return your own RequestCondition

Explicit Registrations

You can programmatically register handler methods, which can be used for dynamic registration or advanced cases. Like different instances of the same processor at different urls.

For example:

@Configuration
public class MyConfig {

    @Autowired
    public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler)
            throws NoSuchMethodException { // Inject the target processor and processor mapping into the controller

        RequestMappingInfo info = RequestMappingInfo
                .paths("/user/{id}").methods(RequestMethod.GET).build(); // Prepare request mapping metadata

        Method method = UserHandler.class.getMethod("getUser", Long.class); // Get the handler method

        mapping.registerMapping(info, handler, method); // Add registration}}Copy the code

Handler Methods

RequestMapping has flexible processor method signatures; You can choose from a list of supported method parameters and return values.

Method Arguments

Reactive types can be supported for parameters that need to handle blocking IO (e.g., Reactor, RxJava, or others). Reactive types are not recommended for parameters that do not require blocking

JDK8’s Optional annotation is used with an annotation with the required attribute, which is equivalent to adding required=false to the annotation

1. ServerWebExchange

Full access to ServerWebExchange. ServerWebExchange is a container containing: HTTP Request and HTTP Response; Request and session properties; CheckNotModified method and others

2. ServerHttpRequest, ServerHttpResponse

Add HTTP Request and Response

3. WebSession

To access the Session; No new session is forced to start unless attributes are added; Reactive types are supported.

4. java.security.Principal

The currently authenticated user – may be the specific Principal implementation class (if known). Reactive types are supported.

5. org.springframework.http.HttpMethod

Requested method

6. java.util.Locale

The current request locale is determined by the most specific available LocaleResolver; In fact is to configure the LocaleResolver/LocaleContextResolver.

7. java.util.TimeZone + java.time.ZoneId

The time zone associated with the current request is determined by LocaleContextResolver.

8. @PathVariable

Used to access URI template variables

9. @MatrixVariable

Used to access key/value pairs in URI path fragments

10. @RequestParam

Used to access Servlet request parameters; The value of the parameter has been converted to the parameter type of the declared method.

Note: use of @requestParam is optional

11. @RequestHeader

Used to access the request header; The value of the request header has been converted to the parameter type of the declared method.

12. @CookieValue

Used to access cookies; The value of the Cookie has been converted to the parameter type of the declared method.

13. @RequestBody

Used to access the HTTP request body; With the HttpMessageReader instance, the request body content has been converted to the parameter type of the declared method. Reactive types are supported.

14. HttpEntity<B>

Access the header and body of the request; The request body is converted to the parameter type of the declared method by the HttpMessageReader instance. Reactive types are supported.

15. @RequestPart

A part used to access a request of type “multipart/form-data”. Reactive types are supported.

16. java.util.Map, org.springframework.ui.Model, and org.springframework.ui.ModelMap.

Use to access the model used in the HTML controller and present it to the template as part of the view rendering.

17. @ModelAttribute

Used to access existing properties (instantiated if none exist) in the model to which data binding and validation are applied.

The use of @modelAttribute is optional.

18. Errors, BindingResult

Errors in validation and data binding used to access command objects.

The Errors or BindingResult arguments must be declared immediately after the validated method arguments.

19. SessionStatus + class-level @SessionAttributes

Used to mark completion of form processing; Clears all Session attributes declared through the class-level @sessionAttributes annotation.

20. UriComponentsBuilder

Used to prepare the URL of Host, Port, Scheme, and Path relative to the current request.

21. @SessionAttribute

Use to access any Session property

22. @RequestAttribute

Used to access request properties.

23. Any other argument

By default, if it is a simple type, it resolves to @requestParam.

Return Values

All return values support reactive types.

1. @ResponseBody

The return value is encoded by HttpMessageWriter and written to the response.

2. HttpEntity<B>, ResponseEntity<B>

The return value specifies the complete response, including the HTTP header and body; And then write to the HTTP Response via HttpMessageWriter.

3. HttpHeaders

Return a response with an HTTP header but no body.

4. String

Returns the template name, which is parsed by the view parser to get the template name.

5. View

Returns a view.

6. java.util.Map, org.springframework.ui.Model

Implicitly determine the view name based on the request path; Attributes are implicitly added to model data.

7. @ModelAttribute

Ready to add to model data; The view name is implicitly inferred from the request path.

8. Rendering

Apis for model and view rendering schemes.

9. void

A method that returns a null value, possibly asynchronous; If the parameter has a ServerHttpResponse, ServerWebExchange, or @responseStatus annotation; That means the method handles the response completely, without returning the value and letting Spring write it out.

Another possibility is, instead of having a ResponseBody, we have a ResponseHeader.

10. Flux<ServerSentEvent>, Observable<ServerSentEvent>, or other reactive type

Send server events; You can omit the ServerSentEvent wrapper when you only need to write data. If you want to use “text/event-stream”; “Text /event-stream” must be requested or declared in the map via the Produces attribute.

11. Any other return value

Type Conversion

For String input requests, if the declared type is not String; Then a cast will occur.

In addition to casting primitives, you can cast them via WebDataBinder; Or registration by FormattingConversionService Formatters to implement custom transformation.

Matrix Variables

Matrix variables, which appear in urIs, with ‘between each variable; ‘space; Each value is separated by a ‘,’; For example: “/ cars; color=red,green; year=2012”; There are three variables: [cars], [color], [year]; [color] corresponds to two values: red and green. Multiple values can be separated to correspond to a variable; For example: “color = red; color=green; Color =blue” means color has three values.

Unlike Spring MVC, the default of matrix variables does not affect the request mapping; If you want to access matrix variables, you can add variables to the URI request path to receive matrix variables, such as:

// GET /pets/42; q=11; r=22

@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {

    // petId == 42
    // q == 11
}
Copy the code

For possible ambiguity, the only variable can be indicated:

// GET /owners/42; q=11/pets/21; q=22

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
        @MatrixVariable(name="q", pathVar="ownerId") int q1,
        @MatrixVariable(name="q", pathVar="petId") int q2) {

    // q1 == 11
    // q2 == 22
}
Copy the code

Optional parameters can also be set to default values:

// GET /pets/42

@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {

    // q == 1
}
Copy the code

You can also use MultiValueMap to get all the values of a matrix variable:

// GET /owners/42; q=11; r=12/pets/21; q=22; s=23

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
        @MatrixVariable MultiValueMap<String, String> matrixVars,
        @MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) {

    / / matrixVars: [" q ": [11, 22]," r ": 12," s ": 23]
    // petMatrixVars: ["q" : 22, "s" : 23]
}
Copy the code

@RequestParam

Unlike Spring MVC, WebFlux’s @RequestParam annotation does not bind query parameters, form data, and file/binary data together; By default, WebFlux’s @RequestParam only has query parameters, so if you want to implement binding, you can use data binding to convert query parameters, form data, and binary/file data into command objects.

Method arguments using @RequestParam annotations are mandatory by default, but you can specify method arguments optionally by setting @RequestParam’s required flag to false or by declaring arguments using the java.util.Optional wrapper.

When the @RequestParam annotation is declared on a Map <String, String> or MultiValueMap <String, String> parameter, the Map is populated with all query parameters.

This annotation is optional, meaning that any primitive type that is not parsed by other parameters is treated as decorated with the @requestParam annotation.

@RequestHeader

You can use this annotation to bind the request header to a method parameter, for example:

Host localhost:8080 Accept text/html,application/xhtml+xml,application/xml; Q = 0.9 Accept - Language fr, en - gb; Q = 0.7, en. Q =0.3 Accept-encoding gzip, Deflate Accept-Charset ISO-8859-1, UTF-8; Q = 0.7 *; Q = 0.7 Keep Alive - 300Copy the code
@GetMapping("/demo")
public void handle(
        @RequestHeader("Accept-Encoding")String encoding, // Gets the accepted encoding type@RequestHeader("Keep-Alive") long keepAlive) { // Gets whether to keep the connection
    / /...
}
Copy the code

Similarly, if a Map or MultiValueMap is used as an argument to the annotation, the Map is automatically populated.

@CookieValue

You can use the @Cookievalue annotation to bind the value of the HTTP cookie to the method parameters in the controller.

@ModelAttribute

Access to model data can be achieved by adding @ModelAttribute annotations in front of method parameters; If this data does not exist, a new instance is created. The values of the model data will override variables of the same name in the query parameters and form data. This is because of data binding, which saves you the hassle of manually parsing individual data.

Adding BindingResult to @ModelAttribute handles data binding exceptions.

Automatic validation can be implemented by adding an @valid annotation in front of @modelAttribute.

@modelAttribute is optional.

@SessionAttributes

This annotation is used to store webSessions in the Model between requests.

This is a type-level annotation that declares the Session property used by a particular controller.

@Controller
@SessionAttributes("pet")
public class EditPetForm {
    // ...} The above code stores pet in the Session. Until another controller uses the SessionStatus method to clear the Session. The following method clears the Session@Controller
@SessionAttributes("pet")
public class EditPetForm {

    // ...

    @PostMapping("/pets/{id}")
    public String handle(Pet pet, BindingResult errors, SessionStatus status) {
        if (errors.hasErrors()) {
            // ...
        }
            status.setComplete();
            // ...}}}Copy the code

@SessionAttribute

If you need access to preexisting session attributes that are managed globally (for example, outside the controller (for example, through filters) and may or may not exist, you can use the @sessionAttribute annotation on method parameters, as shown in the following example:

@GetMapping("/")
public String handle(@SessionAttribute User user) { // Use Session data named user
    // ...
}
Copy the code

Consider injecting webSessions into controller methods to add or remove sessions.

@RequestAttribute

Easier access to previous request attributes using @sessionAttribute; Consider using @requestAttribute, as follows:

@GetMapping("/")
public String handle(@RequestAttribute Client client) { // Access the previous attributes
    // ...
}
Copy the code

Multipart Content

The best way to handle file upload forms is to bind them to command objects via data binding. Such as:

class MyForm {

    private String name;

    private MultipartFile file;

    // ...

}

@Controller
public class FileUploadController {

    @PostMapping("/form")
    public String handleFormUpload(MyForm form, BindingResult errors) {
        // ...}}Copy the code

For a commit:

POST /someUrl
Content-Type: multipart/mixed

--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="meta-data"
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: 8bit

{
    "name": "value"
}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="file-data"; filename="file.properties"
Content-Type: text/xml
Content-Transfer-Encoding: 8bit
... File Data ...
Copy the code

Separate parts can be accessed via @requestPart:

@PostMapping("/")
public String handle(@RequestPart("meta-data")Part metadata, // Access metadata@RequestPart("file-data") FilePart file) { // Access file data
    // ...
}
Copy the code

To deserialize native data, receive data with concrete objects instead of parts:

@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata) { // Deserialize to MetaData
    // ...
}
Copy the code

You can also combine @requestPart and @validated to implement verification; Then use Mono to prepare to handle the exception

@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") Mono<MetaData> metadata) {
    // use one of the onError* operators...
}
Copy the code

To wrap all the data in MultiValueMap form, use the @RequestBody annotation

@PostMapping("/")
public String handle(@RequestBody Mono<MultiValueMap<String, Part>> parts) {
    // ...
}
Copy the code

In streaming data, for continuous access to the data, Flux can be used for processing

@PostMapping("/")
public String handle(@RequestBody Flux<Part> parts) {
    // ...
}
Copy the code

@RequestBody

You can use this annotation to read the HTTP request body or deserialize it into a class; Such as:

@PostMapping("/accounts")
public void handle(@RequestBody Account account) {
    // ...
}
Copy the code

Unlike Spring MVC, in Web Flux, @RequestBody is a responsive and completely non-blocking design; It can also be read using streams, such as:

@PostMapping("/accounts")
public void handle(@RequestBody Mono<Account> account) {
    // ...
}
Copy the code

Standard Spring Bean validation exceptions contain BindingResult with error details, using @Validated; You can declare parameters in controller methods by using asynchronous wrappers; The exception is then handled using the error-associated operator.

HttpEntity

More or less like @requestbody, but it has both a request header and a RequestBody. Such as:

@PostMapping("/accounts")
public void handle(HttpEntity<Account> entity) {
    // ...
}
Copy the code

@ResponseBody

This annotation serializes the return value into the Response Body via HttpMessageWriter. Such as:

@GetMapping("/accounts/{id}")
@ResponseBody
public Account handle(a) {
    // ...
}
Copy the code

The @responseBody at the class level is inherited by the method.

@responseBody supports asynchronous, responsive operations; This means that the result can be written to the Response Body using a response type.

ResponseEntity

ResponseEntity is like @responseBody but with a status code and a return header; Such as:

@GetMapping("/something")
public ResponseEntity<String> handle(a) {
    String body = ... ;
    String etag = ... ;
    return ResponseEntity.ok().eTag(etag).build(body);
}
Copy the code

WebFlux supports asynchronous generation of ResponseEntity or ResponseEntity using a single-valued response type; Single and multi-value response types to generate the body section.

Jackson JSON

1. JSON Views

Model(Model data)

The front and rear ends are separated, temporarily omitted. Basically the same as Spring MVC.

DataBinder(DataBinder)

This applies when Spring cannot automatically transform data.

Managing Exceptions

REST API exceptions

Controller Advice

Functional Points(Functional endpoints)

In addition to annotation methods, functional declarations are also used for business processing.

HTTP requests are processed by handler. A ServerRequest is passed in, which the handler accepts and returns a Mono.

The handler is similar to the @requestMapping method body.

Note that the received and returned data types are fixed, so do not change them arbitrarily.

Requests are routed by the routing function to the corresponding handler method and back to a Mono, so obviously the routing function is like @RequestMapping, but with data and (forward) behavior.

Routerfunctions.route () provides a series of utility methods to build routing functions, Then using RouterFunctions. ToHttpHandler (RouterFunction) transform routing functions into an HttpHandler then install into the built-in ServerAdapter.

HandlerFunction(processor function)

Request to include elements in the form of Flux or Mono; Responses are presented as reactive Publisher, including Flux and Mono.

ServerRequest

ServerRequest provides access to HTTP methods, URIs, headers, and query parameters, as well as the body() method for direct access to the request body.

Extract the request body to Mono:

Mono<String> string = request.bodyToMono(String.class);
Copy the code

Extract the request body to the POJO:

Flux<Person> people = request.bodyToFlux(Person.class);
Copy the code

Use the more generic ServerRequest.body(BodyExtractor) to extract data:

Mono<String> string = request.body(BodyExtractors.toMono(String.class));
Flux<Person> people = request.body(BodyExtractors.toFlux(Person.class));
Copy the code

Accessing form data:

Mono<MultiValueMap<String, String> map = request.formData();
Copy the code

Accessing multipart data:

Mono<MultiValueMap<String, Part> map = request.multipartData();
Copy the code

Accessing multiple parts of multipart data at once:

Flux<Part> parts = request.body(BodyExtractors.toParts());
Copy the code

ServerResponse(ServerResponse)

ServerResponse provides access to the response body, which can be created using the build() method. You can use the builder to set the response status code, add response headers, provide response bodies, and so on.

An example using a 200 status code:

Mono<Person> person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class);
Copy the code

Depending on codec support, you can provide implicit arguments to indicate how the response body should be serialized and deserialized. Such as:

ServerResponse.ok().hint(Jackson2CodecSupport.JSON_VIEW_HINT, MyJacksonView.class).body(...) ;Copy the code

Handler Classes

Request processing can be implemented by writing handlers, such as:

HandlerFunction<ServerResponse> helloWorld =
  request -> ServerResponse.ok().bodyValue("Hello World");
Copy the code

If you need to write more than one handler, consider organizing them into a handler class, like the @Controller class. Such as:

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;

public class PersonHandler {

    private final PersonRepository repository;

    public PersonHandler(PersonRepository repository) {
        this.repository = repository;
    }

    public Mono<ServerResponse> listPeople(ServerRequest request) { 
        Flux<Person> people = repository.allPeople();
        return ok().contentType(APPLICATION_JSON).body(people, Person.class);
    }

    public Mono<ServerResponse> createPerson(ServerRequest request) { 
        Mono<Person> person = request.bodyToMono(Person.class);
        return ok().build(repository.savePerson(person));
    }

    public Mono<ServerResponse> getPerson(ServerRequest request) { 
        int personId = Integer.valueOf(request.pathVariable("id"));
        returnrepository.getPerson(personId) .flatMap(person -> ok().contentType(APPLICATION_JSON).bodyValue(person)) .switchIfEmpty(ServerResponse.notFound().build()); }}Copy the code

The Validation (verification)

You can use the Spring Validation tool to validate the request body.

HandlerFunction(router)

Routing functions are used to route requests to the corresponding handler; It is usually not necessary to write your own routing functions; the RouterFunctions utility class provides a convenient method. Routerfunctions.route () allows you to quickly build a routing function, and RouterFunctions.Route (RequestPredicate, HandlerFunction) provides a more straightforward way to do this.

The route() method is recommended because it provides a more ‘shortcut’ approach. In addition to routing based on HTTP methods, additional assertions are provided to refine the mapping. For each HTTP method, there is an overloaded variant that provides a RequestPredicate argument for additional constraints to make the mapping match more accurately.

Predicates (claim)

The RequestPredicates utility class provides common implementations based on request paths, HTTP methods, content-type, and so on. For example, the following partition is based on Accept:

RouterFunction<ServerResponse> route = RouterFunctions.route()
    .GET("/hello-world", accept(MediaType.TEXT_PLAIN),
        request -> ServerResponse.ok().bodyValue("Hello World")).build();
Copy the code

Also combine your own assertions:

RequestPredicate.and(RequestPredicate) // Simultaneously match.
RequestPredicate.or(RequestPredicate) // As long as there is a match.
Copy the code

Routes (routing)

Router routing rules: Match in order, so you should write the more specific ones first and the more general ones later. Note: This is different from the annotation method, where the annotation method matching principle is best match, so pay attention to the order.

In general, defined routes should be grouped together using RouterFunction. There are several ways to group routes together:

A) Add (RouterFunction) on routerfunctions.route ()

B) Use RouterFunction. And (RouterFunction)

C) RouterFunction.and() and RouterFunctions.route() -RouterFunction.andRoute(RequestPredicate, HandlerFunction)

For example:

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;

PersonRepository repository = ...
PersonHandler handler = new PersonHandler(repository);

RouterFunction<ServerResponse> otherRoute = ...

RouterFunction<ServerResponse> route = route()
    .GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson) 
    .GET("/person", accept(APPLICATION_JSON), handler::listPeople) 
    .POST("/person", handler::createPerson) 
    .add(otherRoute) 
    .build();
Copy the code

1. The Nested Routes were led by…

It is common for multiple routing functions to share the same assertion, for example, the request path. Take the example of a shared path:

RouterFunction<ServerResponse> route = route()
    .path("/person", builder -> builder 
        .GET("/{id}", accept(APPLICATION_JSON), handler::getPerson)
        .GET("", accept(APPLICATION_JSON), handler::listPeople)
        .POST("/person", handler::createPerson))
    .build();
Copy the code

Note that PATH is a consumer consuming the Route Builder. The following example shows nesting if the same Accept attribute is still shared:

RouterFunction<ServerResponse> route = route()
    .path("/person", b1 -> b1
        .nest(accept(APPLICATION_JSON), b2 -> b2
            .GET("/{id}", handler::getPerson)
            .GET("", handler::listPeople))
        .POST("/person", handler::createPerson))
    .build();
Copy the code

Running a Server

One way to run a routing function is to convert the routing function to an HttpHandler and then use a Server Adapter for adaptation.

A more typical approach, and one used by Spring Boot, is to use WebFlux Config to set up a DispatcherHandler-based configuration class. It uses Spring configuration to declare the components needed to process the request. Java Configuration for WebFlux declares the following basic components to support functional endpoints.

RouterFunctionMapping: Infer multiple RouterFunctions <? > bean and group them together via RouterFunction.andOther, and route requests to the combined RouterFunction.

HandlerFunctionAdapter: A simple adapter that lets the DispatcherHandler call a HandlerFunction that matches the request.

ServerResponseResultHandler: call ServerResponse writeTo method to treat calls HandlerFunction results in writing to the response.

The processing component makes functional endpoints more life-cycle friendly for handling DispatcherHandler requests and can also run in parallel with annotation declared controller classes. This is also how the Spring WebFlux initiator enables functional endpoints.

The following example shows how to use the WebFlux Java configuration:

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

    @Bean
    publicRouterFunction<? > routerFunctionA() {// ...
    }

    @Bean
    publicRouterFunction<? > routerFunctionB() {// ...
    }

    // ...

    @Override
    public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
        // configure message conversion...
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // configure CORS...
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        // configure view resolution for HTML rendering...}}Copy the code

Filtering Handler Functions

Before applies to all routers; After applies to all routers; If there is nesting, it only works on the body of the nesting.

URI Links

UriComponents(URI components)

UriBuilder(URI Builder)

URI Encoding(URI Encoding)

CORS(Cross-domain Access)

The Introduction (Introduction)

Processing (Processing)

@CrossOrigin

Global Configuration

CORS WebFilter(Cross-domain Access Filter)

Web Security

View Technologies

Thymeleaf

FreeMarker

View Configuration

FreeMarker Configuration

Form Handling

Script Views

JSON and XML

HTTP Caching

CacheControl CacheControl

Controllers

Static Resources

WebFlux Config(WebFlux configuration)

Enabling WebFlux Config(Enabling WebFlux configuration)

WebFlux Config API

Conversion, formatting

Validation

Content Type Resolvers

HTTP message Codecs

View Resolvers

Static Resources

Path Matching

Advanced Configuration Mode

HTTP/2

WebClient

The Configuration (Configuration)

MaxInMemorySize(Maximum memory size)

Reactor Netty(Reactor Netty)

Resources (resource)

Timeouts (supermarket)

Jetty

Retrieve ()

Exchange ()

Request Body

Form Data

Multipart Data(file/binary)

Client Filters

Synchronous Use

Testing (test)

WebSockets

Introduction to WebSocket

HTTP Versus WebSocket

When to Use WebSockets

WebSocket API(WebSocket Interface)

Server(Server)

WebSocketHandler(WebSocket handler)

DataBuffer(Data buffering component)

Handshake Datagram

Server Configation

CORS(Cross-domain Access)

Client