Spring Web MVC-20 of spring series

Spring Web MVC

Spring Web MVC is an original Web framework based on Servlet API.

In parallel with Spring Web MVC, Spring Framework 5.0 introduces a responsive stack Web framework called "Spring WebFlux"

DispatcherServlet

Like many other Web frameworks, Spring MVC is designed around the front-end controller pattern. One central controller Servlet provides a shared algorithm for dispatcher Servlet request processing, and the actual work is performed by configurable delegate components. The model is very flexible and supports a variety of workflow.

Like DispatcherServletany, servlets need to be configured in Java according to the Servlet specification or on the web xml. In turn, DispatcherServlet uses Spring configuration to discover delegates required for request mapping, view parsing, exception handling, and so on assembly.

The following Java configuration example registers and initializes the automatically detected by the DispatcherServletServlet container:

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(context);
        ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

The following web The XML configuration example registers and initializes the dispatcher servlet:

<web-app>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/app-context.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>

</web-app>

Context hierarchy

For many applications, having a WebApplicationContext is simple and sufficient. You can also have a context hierarchy in which a root WebApplicationContext is shared among multiple DispatcherServlet (or other Servlet) instances, and each instance has its own sub WebApplicationContext configuration.

The root WebApplicationContext typically contains infrastructure beans, such as data repositories and business services that need to be shared across multiple Servlet instances. These beans are effectively inherited and can be overridden (i.e., redeclared) in a Servlet specific child, which usually contains a given local beans Servlet.

The application context is generally divided into two parts: one is servlet WebApplicationContext, which contains some beans related to Spring mvc. In the early stage, some filter s were added when configuring componentScan. Just scan some beans annotated by the controller. On the web Configure the loading of other configuration files in XML.
The servlet WebApplicationContext contains the controller, and the root WebApplicationContext contains the service and repository.
The servlet WebApplicationContext inherits the root WebApplicationContext. If the corresponding bean cannot be found in the servlet, it will be found in the root.

The following example configures the WebApplicationContext hierarchy:

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { App1Config.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/app1/*" };
    }
}

Special Bean

peas and beansexplain
HandlerMappingMatching requests with for preprocessing and postprocessing Interceptor List to the handler. The mapping is based on some standards, and its details vary depending on the HandlerMapping implementation. The two main HandlerMapping implementations are RequestMappingHandlerMapping (supports methods annotated with @ RequestMapping) and simpleurhandlermapping (maintains explicit registration with the handler's URI path pattern).
HandlerAdapterHelp DispatcherServlet calls map to the requested handler, regardless of how the handler is actually called. For example, calling an annotated controller requires parsing the annotation. The main purpose of a HandlerAdapter is to mask the details of DispatcherServlet.
HandlerExceptionResolverStrategies for resolving exceptions, possibly mapping them to handlers, HTML error views, or other targets. see also exception.
ViewResolverString resolves the logical based View name returned from the handler to the actual View name used to render the response. see also View resolution and View technology.
LocaleResolver, LocaleContextResolverParse the possible that Locale customers are using and their time zones so that they can provide an international view. see also Regional settings.
ThemeResolverAddress topics that your Web application can use -- for example, provide a personalized layout. see also theme.
MultipartResolverWith the help of some multipart parsing libraries, parse the abstraction of multipart requests (for example, browser form file upload). see also Multipart parser.
FlashMapManagerFlashMap storage and retrieval can be used to pass attributes from one request to the "input" and "output" of another request, usually through redirection. see also Flash properties.

Web MVC configuration

Spring Boot relies on MVC Java configuration to configure Spring MVC and provides many additional convenience options.

@EnableWebMvc // Enable Spring MVC

Program configuration

In the Servlet 3.0 + environment, you can choose to configure the Servlet container programmatically as an alternative or with the web XML files. The following example registers the dispatcher Servlet:

import org.springframework.web.WebApplicationInitializer;

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) {
        XmlWebApplicationContext appContext = new XmlWebApplicationContext();
        appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");

        ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext));
        registration.setLoadOnStartup(1);
        registration.addMapping("/");
    }
}

It is recommended to use Java based Spring configured applications, as shown in the following example:

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { MyWebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

working principle

The dispatcher servlet process request is as follows:

  • Search for and bind attributes that can be used as controllers and other elements in the process in the WebApplicationContext request. The default binding is in dispatcherservlet WEB_ APPLICATION_ CONTEXT_ Attributekey.
  • The locale parser is bound to the request to let the elements in the process parse the locale to be used when processing the request (rendering the view, preparing the data, etc.). If locale parsing is not required, locale parser is not required.
  • The topic parser is bound to the request, allowing elements such as views to decide which topic to use. If you don't use a theme, you can ignore it.
  • If you specify a multipart file parser, Multiparts in the request are checked. If multiple parts are found, the request is wrapped in a MultipartHttpServletRequest for further processing by other elements in the process.
  • Search for the appropriate handler. If a handler is found, run the execution chain (pre processor, post processor, and controller) associated with the handler to prepare the rendering model. Alternatively, for annotated controllers, you can render the response (inside the HandlerAdapter) instead of returning to the view.
  • If the model is returned, the view is rendered. If there is no return model (possibly because the pre processor or post processor intercepted the request, possibly for security reasons), the view will not be rendered because the request may have completed.

abnormal

If an exception occurs during request mapping or is thrown from the request handler, the DispatcherServlet delegates to the HandlerExceptionResolver bean chain to resolve the exception and provide alternative handling, which is usually an error response.

To customize the default error page of the container, you can xml. The following example shows how to do this:

<error-page>
    <location>/error</location>
</error-page>

Given the previous example, when an exception occurs or the response has an error state, the Servlet container will assign error to the configured URL within the container (for example, / error). The dispatcher Servlet is then processed by the, possibly mapped to @ Controller, which can be implemented as returning the wrong view name with the model or rendering a JSON response, as shown in the following example:

@RestController
public class ErrorController {

    @RequestMapping(path = "/error")
    public Map<String, Object> handle(HttpServletRequest request) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", request.getAttribute("javax.servlet.error.status_code"));
        map.put("reason", request.getAttribute("javax.servlet.error.message"));
        return map;
    }
}

View resolution

Spring MVC defines the ViewResolver and View interfaces, enabling you to render models in a browser without binding you to a specific View technology.

to configure

You can link view parsers by declaring multiple parser bean s and specifying sorting by setting the order property if necessary. Remember that the higher the order attribute, the later the view parser is in the chain.

redirect

The special redirect: prefix in the view name allows you to perform redirection

forward

You can also forward: use the special prefix UrlBasedViewResolver for the view names that are finally resolved by the subclass. This creates an InternalResourceView that executes a requestdispatcher forward().

Content negotiation

ContentNegotiatingViewResolver Instead of parsing the view itself, delegate to another view parser and select a view similar to the representation requested by the client. The representation can be determined according to the Accept header or query parameters (for example, "/ path?format=pdf").

By comparing the requested media type with its associated media type (also known as), the content negotiatingviewresolver selects the appropriate processing request. The first one in the list that has compatibility returns the representation to the client. If the chain cannot provide a compatible view, the list of views specified by the property is queried. The latter option applies to singletons that can render an appropriate representation of the current resource, regardless of the logical view name. The header can contain wildcards (for example), in which case it is a compatible match

filter

FormContentFilter

The spring web module provides FormContentFilter to intercept HTTP PUT, PATCH and DELETE requests with content type. application/x-www-form-urlencoded reads the form data from the request body, and wraps the ServletRequest form data through a series of methods to make the form data available.

Forwarded header

When a request passes through a proxy (such as a load balancer), the host, port, and scheme may change, which makes it a challenge to create a link to the correct host, port, and scheme from the client's point of view.

ForwardedHeaderFilter is a Servlet filter that modifies the request to change the host, port, and scheme based on the Forwarded header, and deletes these headers to eliminate further impact. The filter depends on wrapping the request, so it must be sorted before other filters, such as RequestContextFilter, which should be used with the modified request rather than the original request.

The Forwarded header has security concerns because the application cannot know whether the header was added by the proxy, as expected, or by a malicious client. This is why the proxy of the trust boundary should be configured to remove the untrusted header of the Forwarded from the outside. You can also configure ForwardedHeaderFilter with removeOnly=true, in which case it deletes but does not use headers.

ShallowEtagHeaderFilter

The filter ShallowEtagHeaderFilter creates a "shallow" ETag by caching the contents of the write response and calculating the MD5 hash from it. The next time the client sends, it will perform the same operation, but it will also compare the calculated value with the if none match request header. If they are equal, 304 (NOT_MODIFIED) will be returned.

This policy saves network bandwidth, but not CPU, because a complete response must be calculated for each request.

CORS

Spring MVC provides fine-grained support for CORS configuration through annotations on the controller. However, when used with Spring Security, we suggest that relying on CorsFilter must precede the filter chain of Spring Security.

Annotated controller

Spring MVC provides an annotation based programming model, in which the @ Controller component @ RestController uses annotations to express request mapping, request input, exception handling, etc. Annotated controllers have flexible method signatures and do not have to extend base classes or implement specific interfaces. The following example shows a Controller defined by an annotation:

@Controller
public class HelloController {

    @GetMapping("/hello")
    public String handle(Model model) {
        model.addAttribute("message", "Hello World!");
        return "index";
    }
}

Component scan

You can use the standard Spring bean definition in the Servlet to define the Controller bean WebApplicationContext. The @ Controller stereotype allows automatic detection, which is consistent with Spring's general support for detecting classes in the @ Component classpath and automatically registering bean definitions for them. It also serves as a prototype for annotated classes, indicating their role as Web components.

To enable automatic detection of @ Controller for such bean s, you can add component scanning to the Java configuration, as shown in the following example:

@Configuration
@ComponentScan("org.example.web")
public class WebConfig {

    // ...
}

@RestController is a Combined notes , it is meta annotated, @ Controller and @ ResponseBody indicate a Controller. Each method of @ Controller inherits the @ ResponseBody annotation at the type level, so it is written directly to the response body instead of the view resolution and rendered using the HTML template.

If the controller must implement an interface that is not a Spring Context callback (such as InitializingBean, * Aware, etc.), you may need to explicitly configure a class based proxy.

Request mapping

You can use the @ RequestMapping annotation to map requests to controller methods. It has various properties to match URL s, HTTP methods, request parameters, headers, and media types. You can use it at the class level to express shared mappings, or at the method level to narrow down to specific endpoint mappings.

There is also the HTTP method specific shortcut variant @ RequestMapping:

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

Most controller methods should be mapped to a specific HTTP method instead of using @ RequestMapping, which matches all HTTP methods by default. At the class level, @ RequestMapping is still required to represent the shared mapping.

The following example has type and method level mappings:

@RestController
@RequestMapping("/persons")
class PersonController {

    @GetMapping("/{id}")
    public Person getPerson(@PathVariable Long id) {
        // ...
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void add(@RequestBody Person person) {
        // ...
    }
}
URI pattern

@RequestMapping can use the URL schema mapping method. There are two options:

  • PathPattern   - the pre resolution pattern matching the URL path is also pre resolved to PathContainer The solution is designed for Web usage and can effectively handle encoding and path parameters and match them effectively.
  • AntPathMatcher   - matches the string pattern to the string path. This is the original solution used in the Spring configuration to select Classpaths, file systems, and resources in other locations. It is inefficient, and string path input is a challenge to effectively deal with URL encoding and other problems.

PathPattern is the recommended solution for Web applications and the only choice in Spring WebFlux. Prior to version 5.3, AntPathMatcher was the only choice in Spring MVC and was still the default setting. But the PathPattern can be MVC configuration Enabled in.

PathPattern supports with AntPathMatcher In addition, it supports capture patterns, such as {* spring}, to match 0 or more path segments at the end of the path. PathPattern also limits the use of * * for matching multiple path segments, so it is only allowed at the end of the pattern. This eliminates many ambiguities when selecting the best match pattern for a given request. For complete schema syntax, see PathPattern and AntPathMatcher.

Some example modes:

  • "/ resources/ima?e.png" - matches a character in the path segment
  • "/ resources/*.png" - matches zero or more characters in the path segment
  • "/ resources / * *" - matches multiple path segments
  • "/ projects/{project}/versions" - matches the path segment and captures it as a variable
  • "/ projects/{project:[a-z]+}/versions" - use regular expressions to match and capture variables

The captured URI variable can use @ PathVariable For example:

@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
    // ...
}

You can declare URI variables at the class and method levels

The URI variable is automatically converted to the appropriate type, or the typemismatch exception is thrown. Simple types (int, long, Date, etc.) are supported by default. You can register support for any other data type.

Mode comparison

When multiple patterns match a URL, you must select the best match.

Suffix matching

Starting from 5.3, Spring MVC is no longer executed by default* Suffix pattern matching, where the controller / person mapped to is also implicitly mapped to / person. * Therefore, the path extension is no longer used to interpret the response content type of the request -- for example,, / person Pdf etc. / person xml.

A way to request a content type instead of through the "Accept" header is still useful, such as when typing a URL in a browser. A secure alternative to path extension is to use the query parameter policy. If you must use a file extension, consider using the contentnegotiation configurator The properties of mediaTypes restrict them to a list of explicitly registered extensions.

Consumer type

You can narrow the request mapping to content type according to the request, as shown in the following example:

@PostMapping(path = "/pets", consumes = "application/json") 
public void addPet(@RequestBody Pet pet) {
    // ...
}

Use the consumers property to narrow the mapping by content type.

Consumers you can declare shared properties at the class level. However, unlike most other request mapping attributes, when used at the class level, the method level consumers attribute overrides

Producer media type

You can narrow the request mapping according to the content type list generated by the Accept request header and the controller method, as shown in the following example:

@GetMapping(path = "/pets/{petId}", produces = "application/json") 
@ResponseBody
public Pet getPet(@PathVariable String petId) {
    // ...
}

Use the produces property to narrow the mapping by content type.

Parameter, title

You can narrow the request mapping according to the request parameter conditions. You can test whether the request parameter exists (myParam), exists (! myParam), or has a specific value (myParam=myValue). The following example shows how to test a specific value:

@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") 
public void findPet(@PathVariable String petId) {
    // ...
}

Test whether myParam is equal to myValue.

HTTP header, options

@GetMapping (and @ RequestMapping(method=HttpMethod.GET)) transparently supports HTTP HEAD for request mapping. The controller method does not need to be changed. Response wrapper javax servlet. http. Httpservlet ensures that the content length header is set to the number of bytes written (without actually writing the response).

@GetMapping @RequestMapping(method=HttpMethod.GET)) implicitly maps to and supports HTTP HEAD. An HTTP HEAD request is processed like an HTTP GET, except that instead of writing the body, it calculates the number of bytes and sets the content length header.

By default, HTTP OPTIONS is handled by setting the response header to the list of HTTP methods listed in all methods with a matching URL pattern Allow@ RequestMapping

For @ RequestMapping without HTTP method declaration, the Allow header is set to get, head, post, put, patch, delete and options Controller methods should always declare supported HTTP methods (for example, by using HTTP method specific variants: @ GetMapping, @ PostMapping, etc.).

You can explicitly map this method @ RequestMapping to HTTP HEAD and HTTP OPTIONS, but this is not required in common cases.

Explicit registration

You can register handler methods programmatically, which you can use for dynamic registration or advanced cases, such as different instances of the same handler at different URL s. The following example registers a handler method:

@Configuration
public class MyConfig {

    @Autowired
    public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) 
            throws NoSuchMethodException {

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

        Method method = UserHandler.class.getMethod("getUser", Long.class); 

        mapping.registerMapping(info, handler, method); 
    }
}

Handler method

Method parameters

@RequestBody

Used to access the HTTP request body. Use the implementation to convert the body content to the declared method parameter type HttpMessageConverter.

@RequestParam

Used to access Servlet request parameters, including multipart files.

@PathVariable

Used to access URI template variables.

Return value

@ResponseBody

The return value is converted through HttpMessageConverter and written to the response.

Type conversion

Type conversion is automatically applied according to the configured converter. By default, simple types (int, long, Date, etc.) are supported.

@RequestParam

You can use the @ RequestParam annotation to bind Servlet request parameters (that is, query parameters or form data) to method parameters in the controller.

The following example shows how to do this:

@Controller
@RequestMapping("/pets")
public class EditPetForm {

    // ...

    @GetMapping
    public String setupForm(@RequestParam("petId") int petId, Model model) { 
        Pet pet = this.clinic.loadPet(petId);
        model.addAttribute("pet", pet);
        return "petForm";
    }

    // ...

}

Used for @ RequestParam binding petId

@RequestHeader

You can use the @ RequestHeader annotation to bind the request header to method parameters in the controller.

Consider the following requests with headers:

Host local host: 8080
 accept text/html,application/xhtml+xml,application/xml;q=0.9
 Acceptance language fr,en-gb;q=0.7,en;q=0.3
 Accept code gzip,deflate
 Accept character set ISO-8859-1,utf-8;q=0.7,*;q=0.7
 Keep alive 300

The following example gets the values of the accept encoding and keep alive headers:

@GetMapping("/demo")
public void handle(
        @RequestHeader("Accept-Encoding") String encoding, 
        @RequestHeader("Keep-Alive") long keepAlive) { 
    //...
}
@CookieValue

You can use the @ cookie value annotation to bind the value of an HTTP cookie to a method parameter in the controller.

Consider a request with the following cookie:

JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84

The following example shows how to get a cookie value:

@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) { 
    //...
}
@ModelAttribute

You can use comments on the @ ModelAttribute method parameter to access attributes in the model or instantiate them if they do not exist. The model attribute also overrides the value of the HTTP Servlet request parameter whose name matches the field name. This is called data binding and eliminates the need to parse and transform individual query parameters and form fields. The following example shows how to do this:

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) {
    // method logic...
}

Data binding can cause errors. By default, BindException is thrown. However, to check for such errors in the controller method, you can immediately add a parameter @ ModelAttribute next to BindingResult, as shown in the following example:

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { 
    if (result.hasErrors()) {
        return "petForm";
    }
    // ...
}

javax.validation.Valid you can automatically apply validation after data binding by adding annotations or Spring's @ Validated annotation. The following example shows how to do this:

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { 
    if (result.hasErrors()) {
        return "petForm";
    }
    // ...
}
@SessionAttributes

@SessionAttributes are used to store model attributes in HTTP Servlet sessions between requests. It is a type level annotation that declares the session properties used by a particular controller. This usually lists the name or type of model properties that should be transparently stored in the session for subsequent requests to access.

The following example uses the @ SessionAttributes annotation:

@Controller
@SessionAttributes("pet") 
public class EditPetForm {
    // ...
}
@SessionAttribute

If you need to access pre-existing session attributes managed globally (that is, outside the controller - for example, through filters) and may or may not exist, you can use @ SessionAttribute comments on method parameters, as shown in the following example:

@RequestMapping("/")
public String handle(@SessionAttribute User user) { 
    // ...
}
@RequestAttribute

Like @ SessionAttribute, you can use the @ RequestAttribute annotation to access pre-existing request attributes created previously (for example, through ServletFilter or HandlerInterceptor):

@GetMapping("/")
public String handle(@RequestAttribute Client client) { 
    // ...
}
Redirect properties

By default, all model properties are treated as exposed as URI template variables in the redirect URL. Among the remaining properties, those that are primitive types or collections or arrays of primitive types are automatically appended as query parameters.

If a model instance is prepared specifically for redirection, attaching the original type attribute as a query parameter may be the desired result. However, in annotated controllers, the model can contain other attributes added for rendering purposes (for example, drop-down field values). To avoid the possibility that such attributes appear in the URL, @ RequestMapping method can declare a type of parameter RedirectAttributes and use it to specify the exact attribute RedirectView that can be used for. If the method does redirect, RedirectAttributes the content used. Otherwise, use the content of the model.

The RequestMappingHandlerAdapter provides a flag named ignoreDefaultModelOnRedirect, which you can use to indicate that the default content should not be used if the controller method is redirected. Instead, the controller method should declare a type of attribute, RedirectAttributes, or, if it does not, should not pass any attribute to redirectview Both MVC namespace and MVC Java configuration set this flag to false to maintain backward compatibility. However, for new applications, we recommend setting it to true

Note that the URI template variables in the current request will be automatically available when extending the redirect URL. You do not need to explicitly add their RedirectAttributes through Model or. The following example shows how to define redirection:

@PostMapping("/files/{path}")
public String upload(...) {
    // ...
    return "redirect:files/{path}";
}
MultipartFile

MultipartResolver, the content of the POST request will be parsed and accessed as a regular request parameter. The following example accesses a regular form field and an upload file: multipart / form data

@Controller
public class FileUploadController {

    @PostMapping("/form")
    public String handleFormUpload(@RequestParam("name") String name,
            @RequestParam("file") MultipartFile file) {

        if (!file.isEmpty()) {
            byte[] bytes = file.getBytes();
            // store the bytes somewhere
            return "redirect:uploadSuccess";
        }
        return "redirect:uploadFailure";
    }
}
@RequestBody

You can use the @ RequestBody annotation to read the request body and deserialize it into an Object HttpMessageConverter . the following example uses the @ RequestBody parameter:

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

HttpEntity uses more or less the same as @ RequestBody, but is based on a container object that exposes the request header and body. The following listing shows an example:

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

You can use the annotation on the @ ResponseBody method through HttpMessageConverter Serialize the return to the response body. The following listing shows an example:

@GetMapping("/accounts/{id}")
@ResponseBody
public Account handle() {
    // ...
}

@ResponseBody is also supported at the class level, in which case it is inherited by all Controller methods. This is the effect of @ RestController, which is just a combination of @ Controller and @ ResponseBody.

You can use the @ ResponseBody method with JSON serialization views.

Response entity

ResponseEntity is like @ResponseBody But there are status and title. For example:

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

Model

You can use @ ModelAttribute annotation:

  • In method Method parameters Create or access an Object from the model @ RequestMapping on and bind it to the request WebDataBinder through a.
  • @Controller, as a method level annotation @ ControllerAdvice in one or more classes, helps to initialize the model before any @ RequestMapping method call.
  • Mark it on a @ RequestMapping method, and its return value is a model property.

@ModelAttribute annotations can be applied to methods or method parameters.

The methods annotated by @ ModelAttribute will be executed before each method of the Controller is executed. Therefore, when a Controller contains multiple URL s, it should be used with caution.

You can also use @ ModelAttribute as a method level annotation @ RequestMapping on a method, in which case the return value @ RequestMapping of the method is interpreted as a model attribute. This is usually not required because it is the default behavior in the HTML controller and will be interpreted as the name of the view unless the return value is String@ ModelAttribute can also customize the name of model attribute, as shown in the following example:

@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
    model.addAttribute(accountRepository.findAccount(number));
    // add more ...
}

@GetMapping("/accounts/{id}")
@ModelAttribute
public Account handle() {
    // ...
    return account;
}

The above is equivalent to model addAttribute("account", account);

You can use comments on the @ ModelAttribute method parameter to access attributes in the model or instantiate them if they do not exist. The model attribute also overrides the value of the HTTP Servlet request parameter whose name matches the field name. This is called data binding and eliminates the need to parse and transform individual query parameters and form fields. The following example shows how to do this:

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) {
    // method logic...
}

DataBinder

@The Controller or @ ControllerAdvice class can have WebDataBinder methods for instances initialized by @ InitBinder, which in turn can:

  • Bind request parameters (that is, form or query data) to model objects.
  • Converts string based request values (such as request parameters, path variables, headers, cookie s, etc.) to the target type of controller method parameters.
  • String formats the model object value as a value when rendering an HTML form.

@The InitBinder method can register controller specific Java beans. Propertyeditor or SpringConverter and Formatter components. In addition, you can use MVC configuration To register the Converter and Formatter, type the FormattingConversionService for the global share

@The InitBinder method supports many of the same parameters as the @ RequestMapping method, except for the @ ModelAttribute (command object) parameter. Typically, they use WebDataBinder parameters (for registration) and void return value declarations. The following listing shows an example:

@Controller
public class FormController {

    @InitBinder 
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }

    // ...
}

Alternatively, when you use FormattingConversionService - based settings through sharing, you can reuse the same method and register a controller specific Formatter implementation, as shown in the following example:

@Controller
public class FormController {

    @InitBinder 
    protected void initBinder(WebDataBinder binder) {
        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
    }

    // ...
}

abnormal

@Controller and @ControllerAdvice Class can have @ ExceptionHandler methods to handle exceptions from controller methods, as shown in the following example:

@Controller
public class SimpleController {

    // ...

    @ExceptionHandler
    public ResponseEntity<String> handle(IOException ex) {
        // ...
    }
}

The exception can match the top-level exception being propagated (for example, thrown directly by IOException) or the nested reason in the wrapper exception (for example, IOException wrapped in IllegalStateException). Starting with 5.3, this can match any cause level, whereas previously only direct causes were considered.

For matching exception types, it is best to declare the target exception as a method parameter, as shown in the previous example. When multiple exception methods match, root exception matching is usually better than cause exception matching. More specifically, the ExceptionDepthComparator is used to sort exceptions according to the depth of the exception types thrown.

Alternatively, annotation declarations can narrow down the exception types to match, as shown in the following example:

@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(IOException ex) {
    // ...
}

You can even use a list of specific exception types with very general parameter signatures, as shown in the following example:

@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
    // ...
}

A common requirement for REST services is to include error details in the response body. The Spring Framework does not automatically perform this operation because the representation of error details in the response body is application specific. However, @ RestController can use the @ ExceptionHandler method with the return value of ResponseEntity to set the status and body of the response. These methods can also be declared in the @ ControllerAdvice class to apply them globally.

Enhanced controller

@The ExceptionHandler, @InitBinder, and @ ModelAttribute methods apply only to the class or class hierarchy in which they are declared in @ Controller. Conversely, if they are declared in the @ ControllerAdvice or @ RestControllerAdvice class, they apply to any Controller. In addition, starting with 5.3, the @ ExceptionHandler method @ ControllerAdvice can be used to handle exceptions from any @ Controller or any other handler.

@ControllerAdvice is the @ Component of the meta annotation, so you can Component scan Register as a Spring bean. Use @ RestControllerAdvice ` for meta annotation, which means that the method will render its return value through response body message transformation rather than through HTML view.

@ControllerAdvice annotations have properties that allow you to narrow down the controller and handler assemblies to which they apply. For example:

// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}

@Use of ControllerAdvice annotation

@ControllerAdvice
public class SpringControllerAdvice {
    /**
     * Apply to all methods annotated by @ RequestMapping and initialize the data binder before its execution
     * @param binder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {}

    /**
     * Bind the value to the Model so that the global @ RequestMapping can get the value
     * @param model
     */
    @ModelAttribute
    public void addAttributes(Model model) {
        model.addAttribute("words", "hello world");
    }

    /**
     * Global exception capture processing
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Map errorHandler(Exception ex) {
        Map map = new HashMap();
        map.put("code", 100);
        map.put("msg", ex.getMessage());
        return map;
    }

}

After starting the application, the methods annotated by @ ExceptionHandler, @ InitBinder and @ ModelAttribute will act on the methods annotated by @ RequestMapping. For example, the value set on the method parameter model of the @ ModelAttribute annotation above can be obtained through ModelMap in all methods annotated by @ RequestMapping.

@RequestMapping("/index")
public String index(ModelMap modelMap) {
    System.out.println(modelMap.get("words"));
}

// It can also be obtained through @ ModelAttribute
@RequestMapping("/index")
public String index(@ModelAttribute("words") String words) {
    System.out.println(words);
}

@ExceptionHandler intercepts exceptions and handles them uniformly

@The function of ExceptionHandler is to declare one or more types of exceptions. When the qualified Controller throws these exceptions, it will catch these exceptions, and then process them according to the logic of the marked method, so as to change the returned view information.

@Controller
public class UserController {
    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public void users() {
        throw new RuntimeException("No users.");
    }
}

Use @ InitBinder to bind some custom parameters

For @ InitBinder, the annotation is mainly used to bind some custom parameters. Generally, the parameters we use can be bound through @ RequestParam, @ RequestBody or @ ModelAttribute annotations. However, for some special type parameters, such as Date, their binding Spring does not provide direct support, so we can only specify a converter for them, The parameters of string type in request are converted into parameters of Date type through the converter, so as to be used by the method of @ RequestMapping annotation.

Implementation of registering Date type parameter converter with @ InitBinder

@ControllerAdvice
public class SpringControllerAdvice {
    @InitBinder
    public void globalInitBinder(WebDataBinder binder) {
        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
  }
}
@Controller
public class UserController {
    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public void users(Date date) {
        System.out.println(date); // Tue May 02 00:00:00 CST 2019
  }
}

Use @ ModelAttribute to do some operations before the method is executed

As for the usage of @ ModelAttribute, it can be used to declare methods in addition to the attributes that can be used to convert object types when used for method parameters. If it is declared on a method and combined with @ ControllerAdvice, the method will be executed before the execution of all interface methods within the range specified by @ ControllerAdvice, and the return value of the method marked by @ ModelAttribute can also be supplied to the interface methods that will be called later.

An example of method annotation using @ ModelAttribute annotation

@ControllerAdvice
public class SpringControllerAdvice {
    @ModelAttribute(value = "message")
    public String globalModelAttribute() {
        System.out.println("Added message Global properties.");
        return "Output message Global properties.";
    }
}
@Controller
public class UserController {
    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public void users(@ModelAttribute("message") String message) {
        System.out.println(message);
  }
}

Functional controller

This is a lightweight functional programming model, in which functions are used to route and process requests, and contracts are designed for invariance. It is an alternative to the annotation based programming model.

summary

The HTTP request is processed using HandlerFunction:, which accepts ServerRequest and returns ServerResponse Both request and response objects have immutable contracts that provide JDK 8 friendly access to HTTP requests and responses. HandlerFunction is equivalent to @ RequestMapping method body in annotation based programming model.

The incoming request is routed to a processing function with RouterFunction:, which accepts ServerRequest and returns an Optional HandlerFunction (i.e. Optional < HandlerFunction >). When the routing function matches, a processing function is returned; Otherwise, it is null Optional. RouterFunction is equivalent to @ RequestMapping annotation, but the main difference is that the router function provides not only data, but also behavior.

RouterFunctions.route() provides a router builder to help create a router, as shown in the following example:

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.servlet.function.RequestPredicates.*;
import static org.springframework.web.servlet.function.RouterFunctions.route;

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

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


public class PersonHandler {

    // ...

    public ServerResponse listPeople(ServerRequest request) {
        // ...
    }

    public ServerResponse createPerson(ServerRequest request) {
        // ...
    }

    public ServerResponse getPerson(ServerRequest request) {
        // ...
    }
}

If you register RouterFunction as a bean, for example, by exposing it in the @ Configuration class, it will be automatically detected by the servlet

Processing function

ServerRequest and ServerResponse are immutable interfaces that provide JDK 8-friendly HTTP request and response access, including header, body, method and status code.

Server request

ServerRequest provides access to HTTP methods, URI s, headers and query parameters, while access to the body is provided through the body method.

The following example extracts the request body into a String:

String string = request.body(String.class);

The following example extracts the body into list < Person >, where the Person object is decoded from a serialized form (such as JSON or XML):

List<Person> people = request.body(new ParameterizedTypeReference<List<Person>>() {});

The following example shows how to access parameters:

MultiValueMap<String, String> params = request.params();
Server response

ServerResponse provides access to HTTP responses, and since it is immutable, you can use a build method to create it. You can use the builder to set the response status, add a response header, or provide a body. The following example uses JSON content to create a 200 (OK) response:

Person person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);

The following example shows how to build a 201 (CREATED) response with a Location header but no body:

URI location = ...
ServerResponse.created(location).build();

You can also use asynchronous results as principals to a completable future, Publisher, or ReactiveAdapterRegistry For example:

Mono<Person> person = webClient.get().retrieve().bodyToMono(Person.class);
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);

If not only the body, but also the status or header are based on asynchronous types, you can use the static async method on ServerResponse, which accepts completable future < serverresponse >, publisher < serverresponse >, or any other supported ReactiveAdapterRegistry For example:

Mono<ServerResponse> asyncResponse = webClient.get().retrieve().bodyToMono(Person.class)
  .map(p -> ServerResponse.ok().header("Name", p.name()).body(p));
ServerResponse.async(asyncResponse);

Events sent by the server ServerResponse can be provided through the static sse method of on. This method provides a builder that allows you to send strings or other objects as JSON. For example:

public RouterFunction<ServerResponse> sse() {
    return route(GET("/sse"), request -> ServerResponse.sse(sseBuilder -> {
                // Save the sseBuilder object somewhere..
            }));
}

// In some other thread, sending a String
sseBuilder.send("Hello world");

// Or an object, which will be transformed into JSON
Person person = ...
sseBuilder.send(person);

// Customize the event by using the other methods
sseBuilder.id("42")
        .event("sse event")
        .data(person);

// and done at some point
sseBuilder.complete();
Handler class

We can write the processing function as lambda, as shown in the following example:

HandlerFunction<ServerResponse> helloWorld =
  request -> ServerResponse.ok().body("Hello World");

This is convenient, but we need multiple functions in the application, and multiple inline lambda will become chaotic. Therefore, it is useful to combine related handler functions into a handler class, which plays a similar role as @ Controller's annotation based application. For example, the following classes expose a reactive Person Repository:

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 ServerResponse listPeople(ServerRequest request) { 
        List<Person> people = repository.allPeople();
        return ok().contentType(APPLICATION_JSON).body(people);
    }

    public ServerResponse createPerson(ServerRequest request) throws Exception { 
        Person person = request.body(Person.class);
        repository.savePerson(person);
        return ok().build();
    }

    public ServerResponse getPerson(ServerRequest request) { 
        int personId = Integer.parseInt(request.pathVariable("id"));
        Person person = repository.getPerson(personId);
        if (person != null) {
            return ok().contentType(APPLICATION_JSON).body(person);
        }
        else {
            return ServerResponse.notFound().build();
        }
    }

}
verification

Functional endpoints can use Spring's Validation tool Apply validation to the request body. For example, given a custom Spring Validator Implement Person:

public class PersonHandler {

    private final Validator validator = new PersonValidator(); 

    // ...

    public ServerResponse createPerson(ServerRequest request) {
        Person person = request.body(Person.class);
        validate(person); 
        repository.savePerson(person);
        return ok().build();
    }

    private void validate(Person person) {
        Errors errors = new BeanPropertyBindingResult(person, "person");
        validator.validate(person, errors);
        if (errors.hasErrors()) {
            throw new ServerWebInputException(errors.toString()); 
        }
    }
}

RouterFunction

The router function is used to route requests to the corresponding HandlerFunction Typically, instead of writing your own router functions, you use the methods on the routerfunctions utility class to create one. RouterFunctions.route() (no parameters) provides you with a smooth builder to create router functions, and routerfunctions Route (request predicate, handler function) provides a direct way to create a router.

In general, the route() builder is recommended because it provides a convenient shortcut for typical mapping scenarios without the need for hard to find static imports. For example, the router function builder provides a GET(String, HandlerFunction) method to create a mapping for GET requests; And POST(String, HandlerFunction) posts.

In addition to HTTP method based mapping, the route builder provides a way to introduce additional predicates when mapping to requests. For each HTTP method, there is an overloaded variant with aRequestPredicate as a parameter, which can express additional constraints.

predicate

You can write your own requestpredictions, but the requestpredictions utility class provides common implementations based on request path, HTTP method, content type, etc. The following example uses a request predicate to create a constraint based on the Accept header:

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

You can combine multiple request predicates using the following methods:

  • RequestPredicate. And (requestpredict)   - the two must match.
  • RequestPredicate. Or (request predicate)   -- both can be matched.

Many predicates fromRequestPredicates are combined. For example, requestpredictions Get (string) is defined by requestpredictions Method (httpmethod) and requestpredictions path(String). The example shown above also uses two request predicates, which the builder uses in requestpredicates Get is used internally and combined with the accept predicate.

Route

Router functions are evaluated sequentially: if the first route does not match, the second is evaluated, and so on. Therefore, it makes sense to declare a more specific route before a general route. This is also important when registering the router function as a Spring bean, which will be described later. Note that this behavior is different from the annotation based programming model, which automatically selects the "most specific" controller method.

When using the routing function builder, all defined routes are combined into a RouterFunction from build() There are other ways to combine multiple router functions:

  • add(RouterFunction) in routerfunctions Route() builder
  • RouterFunction.and(RouterFunction)
  • RouterFunction.andRoute(RequestPredicate, HandlerFunction)``RouterFunction.and() - nested shortcut routerfunctions route().

The following example shows the composition of four routes:

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.servlet.function.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();
Nested Route

A set of router functions usually have shared predicates, such as shared paths. In the above example, the shared predicate will be the matching path predicate, / person is used by three routes. When using annotations, you can use @ RequestMapping to map to / person In webmvc FN, path predicates can be shared through methods on the path router function builder. For example, the last few lines of the above example can be improved by using nested routing in the following ways:

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();

Although path based nesting is the most common, you can nest on any type of predicate using the methods on the nest builder. The above still contains some duplicates in the form of shared accept header predicate. We can further improve accept by using this method in combination with nest:

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();

Run server

You usually use MVC ConfigDispatcherHandler Run the router function in a settings based setup, which uses the Spring configuration to declare the components required to process the request. The MVC Java configuration declares the following infrastructure components to support functional endpoints:

  • RouterFunctionMapping: detect one or more routerfunctions in Spring configuration <? > Bean, sort them through they Combine them RouterFunction And other, and route the request to the generated combined RouterFunction
  • HandlerFunctionAdapter: a simple adapter that maps the HandlerFunction to the requested a by calling DispatcherHandler.

The previous components fit the functional endpoint into the dispatcher servlet request processing life cycle and (possibly) run with the annotated controller (if declared). This is also how the Spring Boot Web launcher enables functional endpoints.

The following example shows the WebFlux Java configuration:

@Configuration
@EnableMvc
public class WebConfig implements WebMvcConfigurer {

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

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

    // ...

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // configure message conversion...
    }

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

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

Filter processing function

You can filter after handlers using the,, or before methods on the routing function generator. With annotations, you can achieve similar functionality by using, a, or both filters. The filter applies to all routes built by the builder. This means that the filters defined in nested routes are not applicable to "top-level" routes. For example, consider the following example: @ ControllerAdvice``ServletFilter

RouterFunction<ServerResponse> route = route()
    .path("/person", b1 -> b1
        .nest(accept(APPLICATION_JSON), b2 -> b2
            .GET("/{id}", handler::getPerson)
            .GET(handler::listPeople)
            .before(request -> ServerRequest.from(request) 
                .header("X-RequestHeader", "Value")
                .build()))
        .POST("/person", handler::createPerson))
    .after((request, response) -> logResponse(response)) 
    .build();
filter`The method on the router builder accepts a : An acceptance a And return a`HandlerFilterFunction`Function of. The handler parameter represents the next element in the chain. This is usually the handler to route to, but if more than one is applied, it can also be another filter.`ServerRequest``HandlerFunction``ServerResponse

Now we can add a simple security filter to our route. Suppose we have a security manager that can determine whether to allow the security filter of a specific path. The following example shows how to do this:

SecurityManager securityManager = ...

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))
    .filter((request, next) -> {
        if (securityManager.allowAccessTo(request.path())) {
            return next.handle(request);
        }
        else {
            return ServerResponse.status(UNAUTHORIZED).build();
        }
    })
    .build();

URI link

This section describes the various options available in the Spring Framework to handle URI s.

UriComponents

Spring MVC and Spring WebFlux

UriComponentsBuilder helps build URIs from URI templates with variables, as shown in the following example:

UriComponents uriComponents = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")  
        .queryParam("q", "{q}")  
        .encode() 
        .build(); 

URI uri = uriComponents.expand("Westin", "123").toUri();

The previous examples can be combined into one chain and shortened with buildAndExpand, as shown in the following example:

URI uri = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")
        .queryParam("q", "{q}")
        .encode()
        .buildAndExpand("Westin", "123")
        .toUri();

You can further shorten it by going directly to the URI (which means encoding), as shown in the following example:

URI uri = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")
        .queryParam("q", "{q}")
        .build("Westin", "123");

You can also shorten it further using the full URI template, as shown in the following example:

URI uri = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}?q={q}")
        .build("Westin", "123");

UriBuilder

Spring MVC and Spring WebFlux

UriComponentsBuilder Implement UriBuilder. UriBuilder, in turn, you can use UriBuilderFactory Together, UriBuilderFactory and UriBuilder provide a pluggable mechanism to build URIs from URI templates based on shared configurations such as basic URL s, encoding preferences, and other details.

You can configure the RestTemplate and customize the URI of the WebClient with the aUriBuilderFactory. DefaultUriBuilderFactory is the default implementation of an internal UriBuilderFactory that uses UriComponentsBuilder and exposes shared configuration options.

The following example shows how to configure RestTemplate:

// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;

String baseUrl = "https://example.org";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);

RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);

You can also use DefaultUriBuilderFactory directly. It is similar to using UriComponentsBuilder, but not a static factory method, but an actual instance containing configuration and preferences, as shown in the following example:

String baseUrl = "https://example.com";
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);

URI uri = uriBuilderFactory.uriString("/hotels/{hotel}")
        .queryParam("q", "{q}")
        .build("Westin", "123");

URI encoding

UriComponentsBuilder exposes encoding options at two levels:

Both options replace non ASCII and illegal characters with escaped octets. However, the first option also replaces reserved characters that appear in URI variables.

For most cases, the first option may give the expected results because it treats the URI variable as opaque data to be fully encoded, while the second option is useful if the URI variable does contain reserved characters. The second option is also useful when the URI variable is not extended at all, because it also encodes anything that looks like a URI variable.

The following example uses the first option:

URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
        .queryParam("q", "{q}")
        .encode()
        .buildAndExpand("New York", "foo+bar")
        .toUri();

// Result is "/hotel%20list/New%20York?q=foo%2Bbar"

You can also shorten it further using the full URI template, as shown in the following example:

URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
        .build("New York", "foo+bar");

Relative Servlet request

You can use ServletUriComponentsBuilder to create a URI relative to the current request, as shown in the following example:

HttpServletRequest request = ...

// Re-uses host, scheme, port, path and query string...

ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromRequest(request)
        .replaceQueryParam("accountId", "{id}").build()
        .expand("123")
        .encode();

Controller Link

Spring MVC provides a mechanism to prepare links to controller methods. For example, the following MVC controllers allow you to create links:

@Controller
@RequestMapping("/hotels/{hotel}")
public class BookingController {

    @GetMapping("/bookings/{booking}")
    public ModelAndView getBooking(@PathVariable Long booking) {
        // ...
    }
}

You can prepare links by referencing methods by name, as shown in the following example:

UriComponents uriComponents = MvcUriComponentsBuilder
    .fromMethodName(BookingController.class, "getBooking", 21).buildAndExpand(42);

URI uri = uriComponents.encode().toUri();

Asynchronous request

Spring MVC and Servlet 3.0 asynchronous request processing Extensive integration:

DeferredResult

Enable in Servlet container After the asynchronous request processing function, the controller method can wrap the return value DeferredResult of any supported controller method, as shown in the following example:

@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
    DeferredResult<String> deferredResult = new DeferredResult<String>();
    // Save the deferredResult somewhere..
    return deferredResult;
}

// From some other thread...
deferredResult.setResult(result);

The controller can generate return values asynchronously from different threads -- for example, in response to external events (JMS messages), scheduled tasks, or other events.

Callable

The controller can wrap any supported return value in Java util. concurrent. Callable, as shown in the following example:

@PostMapping
public Callable<String> processUpload(final MultipartFile file) {

    return new Callable<String>() {
        public String call() throws Exception {
            // ...
            return "someView";
        }
    };
}

Then you can pass to configure The TaskExecutor runs the given task to get the return value.

CORS

Spring MVC allows you to handle CORS (cross source resource sharing). This section describes how to do this.

introduce

For security reasons, the browser prohibits AJAX calls to resources other than the current source.

@CrossOrigin

Annotation in @CrossOrigin Enable cross domain requests on annotated controller methods, as shown in the following example:

@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

Global configuration

In addition to fine-grained controller method level configuration, you may also want to define some global CORS configurations. You can use CorsConfiguration in any HandlerMapping However, most applications use MVC Java configuration or MVC XML namespace to do this.

By default, the global configuration enables the following features:

  • All origins.
  • All titles.
  • GET, HEAD, and POST methods.

allowCredentials is not enabled by default because it establishes a trust level that exposes sensitive user specific information (such as cookie s and CSRF tokens) and can only be used when appropriate. When enabled, allowOrigins must be set to one or more specific domains (but not the special value "*"), or the allowOriginPatterns property can be used to match a set of dynamic sources.

maxAge is set to 30 minutes.

Java configuration

To enable CORS in the MVC Java configuration, you can use the CorsRegistry callback, as shown in the following example:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/api/**")
            .allowedOrigins("https://domain2.com")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3")
            .exposedHeaders("header1", "header2")
            .allowCredentials(true).maxAge(3600);

        // Add more mappings...
    }
}

CORS filter

You can use the built-in CorsFilter.

If you try to use corsfilter Spring Security, remember Spring Security Built in Support for CORS.

To configure a filter, pass CorsConfigurationSource to its constructor, as shown in the following example:

CorsConfiguration config = new CorsConfiguration();

// Possibly...
// config.applyPermitDefaultValues()

config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);

CorsFilter filter = new CorsFilter(source);

network security

Spring Security The project provides support for protecting Web applications from malicious attacks.

HDIV Is another Web security framework integrated with Spring MVC.

HTTP cache

HTTP caching can significantly improve the performance of Web applications. The HTTP cache revolves around the cache control response header and subsequent conditional request headers (such as last modified and ETag). Cache control recommends how private (e.g., browser) and public (e.g., proxy) caches cache and reuse responses. If the ETag content is not changed, the header is used to issue a conditional request that may result in 304 (NOT_MODIFIED) without a body. ETag can be seen as a more complex successor to the last modified header.

This section describes the HTTP cache related options available in Spring Web MVC.

CacheControl

CacheControl Provides support for configuring header related settings. Cache control is accepted as a parameter in many places:

although RFC 7234 Cache control describes all possible instructions of the response header, but the CacheControl type adopts a use case oriented approach and focuses on common scenarios:

// Cache for an hour - "Cache-Control: max-age=3600"
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);

// Prevent caching - "Cache-Control: no-store"
CacheControl ccNoStore = CacheControl.noStore();

// Cache for ten days in public and private caches,
// public caches should not transform the response
// "Cache-Control: max-age=864000, public, no-transform"
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic();

controller

The controller can add explicit support for HTTP caching. We recommend this because the lastModified or ETag value of the resource needs to be calculated before it can be compared with the conditional request header. The controller can add ETag header and cache control settings to ResponseEntity `, as shown in the following example:

@GetMapping("/book/{id}")
public ResponseEntity<Book> showBook(@PathVariable Long id) {

    Book book = findBook(id);
    String version = book.getVersion();

    return ResponseEntity
            .ok()
            .cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
            .eTag(version) // lastModified is also available
            .body(book);
}

If the comparison with the conditional request header indicates that the content has not changed, the previous example will send a 304 (NOT_MODIFIED) response with an empty body. Otherwise, ETag and cache control headers are added to the response.

You can also check the conditional request header in the controller, as shown in the following example:

@RequestMapping
public String myHandleMethod(WebRequest request, Model model) {

    long eTag = ... 

    if (request.checkNotModified(eTag)) {
        return null; 
    }

    model.addAttribute(...); 
    return "myViewName";
}

There are three variants for checking conditional requests based on eTag values, lastModified values, or both. For conditional GET and HEAD requests, you can set the response to 304 (NOT_MODIFIED). For conditional POST, PUT and DELETE, you can set the response to 412 (PRECONDITION_FAILED) instead to prevent concurrent modification.

Static resources

You should use static resources with cache control conditional response headers to provide optimal performance. See configuration Static resources part.

ETag filter

You can use the ShallowEtagHeaderFilter to add the "shallow" eTag value calculated from the response content, saving bandwidth but not CPU time.

MVC configuration

The MVC Java configuration and MVC XML namespace provide a default configuration for most applications and a configuration API to customize it.

For more advanced customizations that are not available in the configuration API, see Advanced Java configuration and Advanced XML configuration.

You do not need to know the MVC Java configuration and the underlying bean s created by the MVC namespace. If you want more information, see Special Bean type and Web MVC configuration.

Enable MVC configuration

In Java configuration, you can use the @ EnableWebMvc annotation to enable MVC configuration, as shown in the following example:

@Configuration
@EnableWebMvc
public class WebConfig {
}

MVC configuration API

In Java configuration, you can implement the WebMvcConfigurer interface, as shown in the following example:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    // Implement configuration methods...
}

Type conversion

By default, formatters of various number and date types are installed, and @ DateTimeFormat can be defined from @ NumberFormat through fields.

To register a custom formatter and converter in the Java configuration, use the following command:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}

By default, Spring MVC considers requesting Locale when parsing and formatting date values. This applies to forms where the date is represented as a string with an input form field. However, for the date and time form fields, the browser uses the fixed format defined in the HTML specification. In this case, you can customize the date and time format as follows:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
        registrar.setUseIsoFormat(true);
        registrar.registerFormatters(registry);
    }
}

verification

By default, if it exists in the classpath Bean Validation (for example, Hibernate Validator), register the LocalValidatorFactoryBean as a global Validator For use with controller method parameters, @ Valid and Validated are used on controller method parameters.

In the Java configuration, you can customize the global Validator instance, as shown in the following example:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public Validator getValidator() {
        // ...
    }
}

Note that you can also register the Validator implementation locally, as shown in the following example:

@Controller
public class MyController {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(new FooValidator());
    }
}

Interceptor

In the Java configuration, you can register interceptors to apply to incoming requests, as shown in the following example:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LocaleChangeInterceptor());
        registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
        registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
    }
}

Content type

You can configure how Spring MVC determines the requested media type from the request (for example, Accept header, URL path extension, query parameters, etc.).

By default, only Accept checks the title.

If you must use URL based content type resolution, consider using query parameter policy instead of path extension. For more details, see Suffix matching and Suffix matching and RFD.

In Java configuration, you can customize the content type resolution of the request, as shown in the following example:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.mediaType("json", MediaType.APPLICATION_JSON);
        configurer.mediaType("xml", MediaType.APPLICATION_XML);
    }
}

Message converter

You can customize HttpMessageConverter in Java configuration by overriding (replacing the default converter created by Spring MVC) or overriding (customizing the default converter or adding other converters to the default converter).

The following example adds a custom XML and Jackson JSON converter, ObjectMapper instead of the default converter:

@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                .indentOutput(true)
                .dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
                .modulesToInstall(new ParameterNamesModule());
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
        converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
    }
}

View controller

This is a shortcut that defines the parametrizableviewcontroller to forward to the view immediately upon call. You can use it statically without running Java controller logic before the view generates a response.

The following Java configuration example forwards / requests to the view home named:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
    }
}

view resolver

MVC configuration simplifies the registration of view parsers.

The following Java configuration example configures content negotiation view parsing by using JSP and Jackson as default values for ViewJSON rendering:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.enableContentNegotiation(new MappingJackson2JsonView());
        registry.jsp();
    }
}

Static resources

Resource This option provides a convenient way to provide static resources from a location-based list.

In the next example, given a request beginning with, / resources relative path is used to find and provide static resources / static relative to / public web application root directory or classpath. The service life of these resources is one year to ensure maximum use of browser cache and reduce HTTP requests from browsers. The last modified information is derived from it, and the Resource#lastModified header supports the HTTP conditional request "last modified".

The following listing shows how to do this using Java configuration:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/public", "classpath:/static/")
                .setCacheControl(CacheControl.maxAge(Duration.ofDays(365)));
    }
}

Default Servlet

Spring MVC allows the mapping of dispatcherservlets to / (thus overwriting the mapping of the container's default Servlet), while still allowing static resource requests to be processed by the container's default Servlet. It configures the DefaultServletHttpRequestHandler with a URL mapping / * * and the lowest priority relative to other URL mappings.

This handler forwards all requests to the default Servlet. Therefore, it must remain last in the order of all other URL s. If you use < MVC: annotation driven > Or, if you set up your own custom HandlerMapping instance, make sure that its order property is set to a value lower than DefaultServletHttpRequestHandler, that is, integer MAX_ VALUE.

The following example shows how to enable this feature using the default settings:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

Path matching

You can customize options related to path matching and URL processing.

The following example shows how to customize path matching in a Java configuration:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer
            .setPatternParser(new PathPatternParser())
            .addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class));
    }

    private PathPatternParser patternParser() {
        // ...
    }
}

Advanced Java configuration

@EnableWebMvc imports DelegatingWebMvcConfiguration, where:

  • Provide default Spring configuration for Spring MVC applications
  • Detect and delegate to the WebMvcConfigurer implementation to customize the configuration.

For advanced mode, you can directly delete @ EnableWebMvc and extend from DelegatingWebMvcConfiguration instead of implementing WebMvcConfigurer, as shown in the following example:

@Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {

    // ...
}

Keywords: Front-end Spring mvc

Added by mrneilrobinson on Tue, 18 Jan 2022 15:19:52 +0200