Recently, many interactions have to deal with the native HttpServletRequest and HttpServletResponse. Read body data from HttpServletRequest and encapsulate it into some kind of data structure; write data to HttpServletResponse and respond. The traditional way of writing is very inelegant, so today we introduce you to a more elegant way.

HttpMessageConverter

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

spring mvc

Spring MVC message conversion is done through the implementation of this interface. There are many implementations of HttpMessageConverter.

HttpMessageConverter

Typically Spring MVC handles Form form submissions, JSON, XML, strings, and even Protobuf by the HttpMessageConverter implementation, where the body parameters 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 does the conversion. In Spring IoC (Spring MVC environment) there is also a container HttpMessageConverter that holds the HttpMessageConverters .

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

We can just use it. So how exactly do we use it? Well, first we need to figure out what HttpInputMessage and HttpOutputMessage are for.

HttpInputMessage

HttpInputMessage represents an HTTP input message, consisting of a request header headers and a readable request body body, usually implemented by a server-side HTTP request handle or a client-side HTTP response handle.

HttpInputMessage

And HttpServletRequest is the extension interface of ServletRequest, which provides the request information of HTTP Servlet and also contains the request header and request body, so the two are related. We just need to find out the actual relationship between the two to allow HttpMessageConverter to read and process the request information carried by HttpServletRequest.

ServletServerHttpRequest

And to be honest, we did find it.

ServletServerHttpRequest

ServletServerHttpRequest is not only an implementation of HttpInputMessage, it also holds an HttpServletRequest instance property, and all operations of ServletServerHttpRequest are based on HttpServletRequest. We can inject HttpServletRequest instances into it via constructs so that HttpMessageConverter can handle HttpServletRequest indirectly.

Extracting the request body

The scenario in focus here is the use of HttpMessageConverter in a Servlet filter, which is less recommended in Spring MVC to manipulate HttpServletRequest. I chose FormHttpMessageConverter, which is usually used to handle application/x-www-form-urlencoded requests. We write a filter to intercept the request extract body .

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * 处理 application/x-www-form-urlencoded 请求
 *
 * @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("打印读取到的请求体:{}",read);
        }
    }
}

Then execute a request of type POST with Content-Type of application/x-www-form-urlencoded.

1
2
3
4
5
6
POST /ind HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
Content-Length: 20

a=b123&c=d123&e=f123

The console will print the following message.

1
2021-12-30 6:43:56.409  INFO 12408 --- [nio-8080-exec-1] sfds: 打印读取到的请求体:{a=[b123], c=[d123], e=[f123]}

ServletServerHttpResponse

There is ServletServerHttpRequest and there is ServletServerHttpResponse, the general principle is similar. It happens to be the opposite of ServletServerHttpRequest, so if we need to deal with the response, for example, if we want to write a JSON response via HttpServletResponse, we can probably write it like this.

1
2
3
4
5
ServletServerHttpResponse servletServerHttpResponse = new ServletServerHttpResponse(response);
// 使用json converter
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
//  authentication 指的是需要写的对象实例
mappingJackson2HttpMessageConverter.write(authentication, MediaType.APPLICATION_JSON,servletServerHttpResponse);

Summary

HttpMessageConverter abstracts the HTTP message conversion strategy and can help us handle some request response issues gracefully. One thing to note though is that the request body body can only be read once, even if it is wrapped in ServletServerHttpRequest, so be aware of the difference with HttpServletRequestWrapper.

Reference https://my.oschina.net/10000000000/blog/5389683