Strategies for processing Request and Response in Spring MVC

Many interactions recently have to deal with native HttpServletRequest and HttpServletResponse. Read body data from HttpServletRequest and encapsulate it into a certain data structure; write data to HttpServletResponse and respond. The traditional way of writing is very inelegant. Today I will introduce a more elegant way for you.

HttpMessageConverter

HttpMessageConverter is a message converter model provided by the Spring framework, a strategy interface for converting between HTTP requests and responses. It can read the input message HttpInputMessage; it can also write the output message HttpOutputMessage.

HttpMessageConverter

The message conversion of Spring MVC is done through the implementation of this interface. HttpMessageConverter has many implementations:

Common implementations of HttpMessageConverter

Typically Spring MVC handles Form form submissions, JSON, XML, strings, and even Protobuf< /strong> are all completed by the implementation of HttpMessageConverter, the body parameter passed from the front end to the back end, and the data returned from the back end to the front end are all converted by this interface. In Spring IoC (Spring MVC environment) there is also a container HttpMessageConverters that stores HttpMessageConverter:

 @Bean
    @ConditionalOnMissingBean
    public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
        return new HttpMessageConverters((Collection)converters.orderedStream().collect(Collectors.toList()));
    }

We can use it directly. So how to use it? First of all, we must figure out what HttpInputMessage and HttpOutputMessage are for.

HttpInputMessage

HttpInputMessage represents an HTTP input message, which consists of request header headers and a readable request body body, usually Implemented by a HTTP request handler on the server side or an HTTP response handler on the client side.

HttpInputMessage

And HttpServletRequest is the extension interface of ServletRequest, which provides the request information of HTTP Servlet, and also includes the request header and request body, so the two are have connection. As long as we find out the actual relationship between the two, we can let HttpMessageConverter read and process the request information carried by HttpServletRequest.

ServletServerHttpRequest

To be honest, I found:

ServletServerHttpRequest

ServletServerHttpRequest is not only the implementation of HttpInputMessage, it also holds a HttpServletRequest instance attribute, all operations of ServletServerHttpRequest They are all based on HttpServletRequest. We can inject HttpServletRequest instance into it through construction, so that HttpMessageConverter can process HttpServletRequest indirectly.

Actual extraction of request body

The focused scenario here is to use HttpMessageConverter in the Servlet filter. It is not recommended to operate HttpServletRequest in Spring MVC. I chose FormHttpMessageConverter, which is usually used to handle application/x-www-form-urlencoded requests. We write a filter to intercept requests to extract body:

/**
 * Handle application/x-www-form-urlencoded requests
 *
 * @author felord.cn
 */

@Component
public class FormUrlencodedFilter implements Filter {
    private final FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
    private static final Logger log = LoggerFactory.getLogger(FormUrlencodedFilter.class);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException {
        String contentType = request. getContentType();
        MediaType type= StringUtils.hasText(contentType)? MediaType.valueOf(contentType):null;
        ServletServerHttpRequest serverHttpRequest = new ServletServerHttpRequest((HttpServletRequest) request);
        
        if (formHttpMessageConverter. canRead(MultiValueMap. class, type)) {
            MultiValueMap<String, String> read = formHttpMessageConverter.read(null, serverHttpRequest);
             log.info("Print the read request body: {}", read);
        }
    }
}

Then execute a request of POST type, Content-Type is application/x-www-form-urlencoded:

POST /ind HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
Content-Length: 20

a=b123 &c=d123&e=f123

Console prints:

Print the read request body: {a=[b123], c=[d123], e=[f123]}

ServletServerHttpResponse

There is ServletServerHttpRequest and there is ServletServerHttpResponse, the general principle is similar. It is exactly the opposite of ServletServerHttpRequest. If we need to deal with the response problem, for example, if we want to write a JSON response through HttpServletResponse, we can probably write it like this:

ServletServerHttpResponse servletServerHttpResponse = new ServletServerHttpResponse(response);
// use json converter
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
// authentication refers to the object instance that needs to be written
mappingJackson2HttpMessageConverter.write(authentication, MediaType.APPLICATION_JSON,servletServerHttpResponse);

Summary

HttpMessageConverter abstracts the strategy of HTTP message conversion, which can help us handle some request response problems gracefully. But one thing to note is that the request body body can only be read once, even if it is wrapped in ServletServerHttpRequest, pay attention to the difference from HttpServletRequestWrapper.

syntaxbug.com © 2021 All Rights Reserved.