The background,

I suddenly encountered this problem in the project before, the display background interface reported this error:

Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type java.math.BigDecimal from String “1,122.00”: not a valid representation at [Source: (PushbackInputStream); line: 23, column: 21] (through reference chain: com.ufgov.ar.modules.bill.bean.vo.ArBillVo[“arBill”]->com.ufgov.ar.modules.bill.bean.ArBill[“billMoney”])

Then I took another approach: Jackson provides annotations @jsonserialize and @jsondeserialize, which are addressed by deserializing BigDecimal and overwriting them. If you have a problem with BigDecimal, see Jackson for more details on this demo.

Second, the introduction of

For the above mentioned problem, because I want to use filters for data filtering, so this article mainly explains the spring-boot filter to read the data in the body, because my piece only applies to the POST request and the content-type is application/json. So there’s no other way to request it.

Three, use of HttpServletRequestWrapper

Here we can custom class inheritance HttpServletRequestWrapper class (the class inherits the ServletRequestWrapper), rewrite getReader () method for processing, Because the controller uses @requestBody to deserialize the object and that’s how the data is read, you can modify the data here to eventually filter the data.

Here are two images to see the order in which requests are received:

Without further ado, insert the code that reads the contents of the body directly into the filter:

First you need to add @ServletComponentScan to the bootstrap class for the filter to work;

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; @SpringBootApplication @ServletComponentScan public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}Copy the code

Filter code:

import cn.hutool.core.util.StrUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.HttpMethod; import java.io.IOException; /** * @className: HttpReplaceFilter * @description: filter * @date: 2020/6/16 14:37 */ @webFilter (filterName ="httpReplaceFilter", urlPatterns = "/ *")
@Component
public class HttpReplaceFilter implements Filter {

    @Autowired
    private IArBillCharacterService arBillCharacterService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String method = httpServletRequest.getMethod();
        String contentType = httpServletRequest.getContentType();
        if(StrUtil.isNotEmpty(contentType)) { contentType = contentType.toLowerCase(); } // This method handles POST requests and contentType is in application/json formatif(HttpMethod.POST.equalsIgnoreCase(method) && StrUtil.isNotEmpty(contentType) && contentType.contains(MediaType.APPLICATION_JSON_VALUE)) { servletRequest = new BodyRequestWrapper(httpServletRequest); } filterChain.doFilter(servletRequest, servletResponse); }}Copy the code

Inheritance HttpServletRequestWrapper rewrite getReader code:

import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.ws.rs.HttpMethod; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; /** * @ProjectName: ar-server * @Package: com.ufgov.ar.conf.filter * @ClassName: BodyRequestWrapper * @Author: Tianmengwei * @description: Read the content information in the Request body * @date: 2020/6/16 14:42 */ public class BodyRequestWrapper extends HttpServletRequestWrapper { private byte[] body; public BodyRequestWrapper(HttpServletRequest request) throws IOException { super(request); String method = request.getMethod(); String requestURI = request.getRequestURI(); requestURI = requestURI.replaceAll("[^A-Za-z\\d]".""); boolean exist = CommonEnums.FilterUrlEnum.exist(requestURI); BufferedReader Reader = request.getreader (); StringBuilder stringBuilder = new StringBuilder(); String line = null;while((line = reader.readLine()) ! = null) { stringBuilder.append(line); } String json = stringBuilder.toString();if(exist && HttpMethod. POST. EqualsIgnoreCase (method)) data processing} {/ /else {
            body = json.getBytes();
        }
    }

    @Override
    public BufferedReader getReader() {
        returnnew BufferedReader(new InputStreamReader(getInputStream())); } /** * @requestBody {getInputStream();} /** * @requestBody; So we'll Override this method * @date 2020/6/16 14:45 */ @override public ServletInputStreamgetInputStream() {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() {
                returnbyteArrayInputStream.read(); }}; }}Copy the code

I can filter the JSON string I get in the above and then return it, which is what I need now. The code here is just about the POST request to read the body data.

Four, the coding problem in the filter:

After the function was implemented for a period of time, there was a feedback that the interface was wrong during the implementation of the project, as follows:

org.springframework.http.converter.HttpMessageConversionException: JSON conversion problem: Invalid UTF-8 start byte 0xb9; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Invalid UTF-8 start byte 0xb9

Roughly speaking: The value of the coName field in the ArBill entity class was wrong when it was parsed by the filter. At first, I thought the Chinese characters that were parameters were garbled, but when I looked at the input parameters, I found that they were all normal. At this time, I thought it might be related to the encoding of the startup script. Encoding =GBK (-dfile. encoding=GBK);

Idea can be configured with the following parameters:

Here I will read the code comment in the request body. Spring is processing the code set according to the UTF-8 encoding in the request header.

Here I set the code set to be consistent with the system Settings. The data is stored to the database and the filter is read properly.

So the problem is that I need to actively set the encoding set to be consistent with the system when I read the filter, otherwise I will fail. But forget the filter part, Spring reads the utF-8 encoding when I read the filter, and then where do I do the processing? The data store is normal. But when I look at the source code and I can't find this setting, I feel confused, right? The big guy that encounters this problem requests guidance!