SpringBoot(06) -- Web Development -- Data Response and Content Negotiation

SpringBoot2 study notes

source address

4. Web development

4.5) Data response and content negotiation

 4.5.1) Response JSON

4.5.1.1)jackson.jar+@ResponseBody

In the POM.xml file, configure spring-boot-starter-web

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

In the spring-boot-starter-web-2.3.4.RELEASE.pom.xml file, configure spring-boot-starter-json

<!--web场景自动引入了json场景-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>

In the spring-boot-starter-json-2.3.4.RELEASE.pom.xml file, configure jackson to automatically return json data to the front end;

 <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.11.2</version>
      <scope>compile</scope>
    </dependency>

4.5.1.1.1) Return Value Parser

 The relevant source code is as follows:

try {
            this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }
        
——————————————————————————————————————————————————————————————————————————————————
    @Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
        if (handler == null) {
            throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
        }
        handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }
——————————————————————————————————————————————————————————————————————————————————  
RequestResponseBodyMethodProcessor      
@Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
        mavContainer.setRequestHandled(true);
        ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
        ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
        // Try even with null return value. ResponseBodyAdvice could get involved.
        // 使用消息转换器进行写出操作
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }

4.5.1.1.2) Return Value Parser Example

Create a new ResponseTestController.java in the project SpringBootDemo3, the code is as follows:

@Controller
public class ResponseTestController {
    //利用返回值处理器里面的消息转换器进行处理
    @ResponseBody
    @GetMapping(value = "/test/person")
    public Person getPerson() {
        Person person = new Person();
        person.setAge(28);
        person.setBirth(new Date());
        person.setUserName("zhangsan");
        return person;
    }
}

Test: start the project, browser access: http://localhost:8080/test/person , the page is as follows:

 4.5.1.1.3) Return Value Parser Principle

  1. The return value processor determines whether to support this type of return value supportsReturnType

  2. The return value processor calls handleReturnValue for processing

  3. RequestResponseBodyMethodProcessor can handle the return value marked with @ResponseBody annotation.


    Use MessageConverters to process and write data as json

    1. Content negotiation (by default, the browser will tell the server what content type it can accept in the form of a request header)

    2. The server finally decides what kind of content data the server can produce according to its own capabilities.

    3. SpringMVC will traverse the underlying HttpMessageConverter of all containers one by one to see who can handle it?

    • Get MappingJackson2HttpMessageConverter can write object as json

    • Use MappingJackson2HttpMessageConverter to convert the object to json and write it out

4.5.1.2) What return values ​​does SpringMVC support

ModelAndView Model View ResponseEntity ResponseBodyEmitter StreamingResponseBody HttpEntity HttpHeaders Callable DeferredResult ListenableFuture CompletionStage WebAsyncTask has @ModelAttribute and is an object type @ResponseBody annotation ---> RequestResponseBodyMethodProcessor;

4.5.1.3) Principle of HTTPMessageConverter

4.5.1.3.1) MessageConverter specification

HttpMessageConverter: Message converter, to see if it supports converting Class type objects to MediaType type data, for example: Person object to JSON or JSON to Person [process is reversible]

 4.5.1.3.2) Default MessageConverter

 

0 - only supports Byte type

1 - String

2 - String

3 - Resource

4 - ResourceRegion

5 - DOMSource.class \ SAXSource.class) \ StAXSource.class \ StreamSource.class \ Source.class

6 - MultiValueMap

7 - true

8 - true

9 - Support annotation mode xml processing

Finally, MappingJackson2HttpMessageConverter converts the object to JSON (converted using the underlying jackson's objectMapper)

 4.5.1.4) Content negotiation

Depending on the receiving capability of the client, data of different media types is returned. [By traversing all MessageConverters, finally find a MessageConverter suitable for processing the media type data for processing]

4.5.1.4.1) Introduce xml dependency

Modify the POM.xml file, the code is as follows:

        <!-- 引入xml依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>

4.5.1.4.2) postman test returns json and xml respectively

Just change the Accept field in the request header. As stipulated in the Http protocol, tell the server the type of data that the client can receive

Set it to application/json, as shown below:

 Get the response message:

{
    "userName": "zhangsan",
    "age": 28,
    "birth": "2022-06-06T03:45:16.408+00:00",
    "pet": null
}

Set it to application/xml, as shown below:

 Get the response message:

<Person>
    <userName>zhangsan</userName>
    <age>28</age>
    <birth>2022-06-06T03:48:54.631+00:00</birth>
    <pet/>
</Person>

4.5.1.4.3) Enable the content negotiation function of the browser parameter method

To facilitate content negotiation, enable the content negotiation function based on request parameters, modify the configuration file application.yaml, the code is as follows:

spring:
  mvc:
    contentnegotiation:
        # 开启浏览器参数方式内容协商功能
      favor-parameter: true

Test: start the project, browser access address: http://localhost:8080/test/person?format=json , the page content is displayed in json format, browser access address: http://localhost:8080/test/person? format=xml , the page content is displayed in xml format, and the page is as follows:

 

 The source code during the call is as follows:

Determine what content type the client receives;

  1. The priority of the Parameter strategy is to return json data (get the value of the format in the request header)

 Finally, perform content negotiation and return the json to the client.

4.5.1.4.4) Content negotiation principle

  1. Determine whether there is already a certain media type [MediaType] in the current response header;

  2. Obtain the content type supported by the client (PostMan, browser); (obtain the client Accept request header field) [application/xml]

    ontentNegotiationManager: The content negotiation manager uses a request header-based strategy by default

 HeaderContentNegotiationStrategy: Determines the type of content the client can receive

  1. Traverse all the MessageConverters of the current system to see who supports the operation of this object (Person)

  2. Find a converter that supports the operation of Person, and count the media types supported by the converter.

  3. The client needs [application/xml], and the server capability [10 types, json, xml]

  1. Best match media type for content negotiation

  2. Use the converter that supports converting the object to the best matching media type , and call it to convert

 Import the jackson package for processing xml, and the xml converter will come in automatically

WebMvcConfigurationSupport
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
if (jackson2XmlPresent) {
            Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
            if (this.applicationContext != null) {
                builder.applicationContext(this.applicationContext);
            }
            messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
        }

4.5.1.5) Custom MessageConverter

Realize multi-protocol data compatibility. json, xml, x-study

  1. @ResponseBody: Response data out, call RequestResponseBodyMethodProcessor processing;

  2. Processor: The return value of the processing method is processed by MessageConverter ;

  3. All MessageConverters together can support the operation (reading and writing) of various media types of data;

  4. Content negotiation finds the final messageConverter ;

Add StudyMessageConverter.java to the project, the code is as follows:

/**
 * 自定义的Converter
 */
public class StudyMessageConverter implements HttpMessageConverter<Person> {
    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return false;
    }
    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return clazz.isAssignableFrom(Person.class);
    }
    /**
     * 服务器要统计所有MessageConverter都能写出哪些内容类型
     * <p>
     * application/x-study
     *
     * @return
     */
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/x-study");
    }
    @Override
    public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }
    @Override
    public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        //自定义协议数据的写出
        String data = person.getUserName() + ";" + person.getAge() + ";" + person.getBirth();
        //写出去
        OutputStream body = outputMessage.getBody();
        body.write(data.getBytes());
    }
Modify the configuration file WebConfig.java, the code is as follows:
            // 拓展 MessageConverter[内容协商策略]
            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new StudyMessageConverter());
            }

Use Postman to test, modify the Accept in Headers to: application/x-study, the test results are as follows:

 

 The source code during operation is as follows:

How to access content negotiation configuration form in the browser by customizing the request header parameters?

Modify the configuration file WebConfig.java, the code is as follows:

           /**
             * 自定义内容协商策略
             * @param configurer
             */
            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
                //支持的媒体类型:Map<String, MediaType> mediaTypes
                Map<String, MediaType> mediaTypes = new HashMap<>();
                mediaTypes.put("json",MediaType.APPLICATION_JSON);
                mediaTypes.put("xml",MediaType.APPLICATION_XML);
                //新增study媒体类型
                mediaTypes.put("study",MediaType.parseMediaType("application/x-study"));
                //指定支持解析哪些参数对应的哪些媒体类型
                ParameterContentNegotiationStrategy parameterStrategy = new ParameterContentNegotiationStrategy(mediaTypes);
                HeaderContentNegotiationStrategy headeStrategy = new HeaderContentNegotiationStrategy();
                configurer.strategies(Arrays.asList(parameterStrategy,headeStrategy));
            }
        };
    }

Test: start the project, browser access address: http://localhost:8080/test/person?format=study , the content of the page is as follows:

 

Guess you like

Origin blog.csdn.net/jianghao233/article/details/125148153