Home >Web Front-end >JS Tutorial >SpringMVC restful annotation @RequestBody converts json and object_javascript skills

SpringMVC restful annotation @RequestBody converts json and object_javascript skills

WBOY
WBOYOriginal
2016-05-16 15:26:222604browse

Due to the upcoming Chinese New Year, the project team did not have many tasks, so I had some free time to study restful calls in spring. I found that spring has become so powerful that programmers no longer need to care about data conversion and invocation in the process of writing interfaces, and only need to focus on business. Below I summarize the steps and the problems encountered during the research process.

Steps:

1. git clone https://github.com/spring-guides/gs-rest-service.git Downloaded the source code from the spring official website

2. Compile with maven (gradle is also acceptable)

3. Run and visit http://localhost:8080/greeting

4. The running result can convert the object into a json object and return it to the page

At this time, I was thinking about how to automatically convert the requested data into a java object. Through google, I found that spring has actually provided the HttpMessageConverter converter, and MappingJackson2HttpMessageConverter (json ~object conversion class) is loaded by default. ). Just configure @RequestBody Greeting gree to use.

The controller layer code is as follows:

@RequestMapping(value = "/greeting", method = RequestMethod.POST,consumes = "application/json")
  public @ResponseBody Greeting greeting(@RequestBody Greeting gree) { 
    System.out.println(gree.getContent());
    return gree;
  }

At this time, I made a call through Google’s plug-in (postman), but the life-or-death call was unsuccessful!

Analyze and solve problems:

At this time I feel that the cause of the problem may be in the following aspects:

1. Spring does not load MappingJackson2HttpMessageConverter by default (I don’t know the specific loading method)

2. MappingJackson2HttpMessageConverter cannot work after loading (I don’t know the reason why it does not work)

In fact, the reason why it didn’t work in the end was that I believed too much in the source code of spring (the object did not provide a set method). With these two questions, massive searchers on the Internet could not find the corresponding results. There is no way but to find the root cause of the problem and look at the spring source code.

For the first question:

Step 1: Manually rewrite the loading type converter

@Configuration
  @EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
  public void configureMessageConverters(List<HttpMessageConverter<&#63;>> messageConverters) {
    System.out.println("init convert is start !!!!!");
    StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
    stringConverter.setWriteAcceptCharset(false);
    messageConverters.add(new MappingJackson2HttpMessageConverter());
    System.out.println("init convert is stop !!!!!");
  }
}

The test found that it still cannot be used, and now the reason is even less clear. You can only see how spring loads the type converter by default. It turns out that in the method addDefaultHttpMessageConverters in WebMvcConfigurationSupport (the keyword HttpMessageConverter is reflected and searched for where it is used, it is found through judgment and tracking) as follows:

@SuppressWarnings("deprecation")
  protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<&#63;>> messageConverters) {
    StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
    stringConverter.setWriteAcceptCharset(false);
    messageConverters.add(new ByteArrayHttpMessageConverter());
    messageConverters.add(stringConverter);
    messageConverters.add(new ResourceHttpMessageConverter());
    messageConverters.add(new SourceHttpMessageConverter<Source>());
    messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    if (romePresent) {
      messageConverters.add(new AtomFeedHttpMessageConverter());
      messageConverters.add(new RssChannelHttpMessageConverter());
    }
    if (jaxb2Present) {
      messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
    }
    if (jackson2Present) {
      messageConverters.add(new MappingJackson2HttpMessageConverter());
    }
    else if (jacksonPresent) {
      messageConverters.add(new org.springframework.http.converter.json.MappingJacksonHttpMessageConverter());
    }
  }

The corresponding default converter has been loaded. Breakpoint debugging shows that there is no problem with the default configuration.

It can only be said that it is caused by the second problem, but I don’t know why it caused this problem (json data problem, or other problems). Without knowing the problem, I can only look at the request request and see how the converter works. working. Because I don't know much about spring, I don't know its principle. In this case, the corresponding usage can only be found based on the (HttpMessageConverter) key class. Use experience to judge and debug. It is found that the readWithMessageConverters method in AbstractMessageConverterMethodArgumentResolver is the processing method for request request to perform type conversion.

protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
      MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
    MediaType contentType;
    try {
      contentType = inputMessage.getHeaders().getContentType();
    }
    catch (InvalidMediaTypeException ex) {
      throw new HttpMediaTypeNotSupportedException(ex.getMessage());
    }
    if (contentType == null) {
      contentType = MediaType.APPLICATION_OCTET_STREAM;
    }
    Class<&#63;> contextClass = methodParam.getContainingClass();
    Class<T> targetClass = (Class<T>) ResolvableType.forType(targetType,
        ResolvableType.forMethodParameter(methodParam)).resolve();
    for (HttpMessageConverter<&#63;> converter : this.messageConverters) {
      if (converter instanceof GenericHttpMessageConverter) {
        GenericHttpMessageConverter<&#63;> genericConverter = (GenericHttpMessageConverter<&#63;>) converter;
        if (genericConverter.canRead(targetType, contextClass, contentType)) {
          if (logger.isDebugEnabled()) {
            logger.debug("Reading [" + targetType + "] as \"" +
                contentType + "\" using [" + converter + "]");
          }
          return genericConverter.read(targetType, contextClass, inputMessage);
        }
      }
      if (targetClass != null) {
        if (converter.canRead(targetClass, contentType)) {
          if (logger.isDebugEnabled()) {
            logger.debug("Reading [" + targetClass.getName() + "] as \"" +
                contentType + "\" using [" + converter + "]");
          }
          return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
        }
      }
    }
    throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
  }

At this time, it was discovered that the corresponding type message converter MappingJackson2HttpMessageConverter had been found according to the canRead method of HttpMessageConverter, and the conversion had started, but a runtime exception was thrown. Because the exception is not output on the console. Through breakpoint debugging, I found that the readJavaType method of MappingJackson2HttpMessageConverter threw a runtime exception. Through the source code, I found that the bottom layer is operated by Jackson's objectMapper. The code is as follows:

try {
      return this.objectMapper.readValue(inputMessage.getBody(), javaType);
    }
    catch (IOException ex) {
      throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
    }

If I take out the code separately and run it in the main method, but it still doesn’t work, then I can locate the problem. Either the type is wrong or the input data is wrong. After careful inspection, I found that there is no problem with the json data, and it can also be converted using jsonobject. At this time, it can only be judged that there is a problem with the incoming javaType. If I open it and find that the object (Greeting) does not have a set method, I wonder if it is because jakson cannot work (the principle is not clear). If so, I provide the set method for this object and it can be run again. After going around in a circle, I finally solved the problem, but through this problem, I became more aware of the working mechanism of spring's restful.

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn