SpringBoot Web开发(三)

数据响应与内容协商

1.响应JSON

1.1 jackson.jar+@ResponseBody

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

        </dependency>

	//web场景自动引入了json场景

    <dependency>
	
	      <groupId>org.springframework.boot</groupId>
	
	      <artifactId>spring-boot-starter-json</artifactId>
	
	      <version>2.3.4.RELEASE</version>
	
	      <scope>compile</scope>

    </dependency>

在这里插入图片描述
给前端自动返回json数据。

1. 返回值处理器

在这里插入图片描述

try {
    
    
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    
    
        HandlerMethodReturnValueHandler handler = this.selectHandler(returnValue, returnType);
        if (handler == null) {
    
    
            throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
        } else {
    
    
            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);
	}
2. 返回值处理器原理

在这里插入图片描述

  • 返回值处理器判断是否支持这种类型返回值 supportsReturnType
  • 返回值处理器调用 handleReturnValue 进行处理
  • RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的
    • 利用 MessageConverters 进行处理 将数据写为json
      • 内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
      • 服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
      • SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?
        • 得到MappingJackson2HttpMessageConverter可以将对象写为json
        • 利用MappingJackson2HttpMessageConverter将对象转为json再写出去

1.2 HTTPMessageConverter原理

1. MessageConverter规范

在这里插入图片描述

HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。
例子:Person对象转为JSON。或者 JSON转为Person

2. 默认的MessageConverter

在这里插入图片描述

0 - 只支持Byte类型的
1 - String
2 - String
3 - Resource
4 - ResourceRegion
5 - DOMSource.class \ SAXSource.class) \ StAXSource.class \StreamSource.class \Source.class
6 - MultiValueMap
9 - 支持注解方式xml处理的

最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)
在这里插入图片描述

2. 内容协商

根据客户端接收能力的不同,SpringMVC自动返回不同类型的数据。
在这里插入图片描述

2.1 引入xml依赖

 <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

2.2 内容协商原理

  • 判断当前响应头中是否已有确定的媒体类型(MediaType)
    在这里插入图片描述

  • 获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)

    • contentNegotiationManager 内容协商管理器 默认使用基于请求头的策略
      在这里插入图片描述
    • HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型
  • 遍历循环所有当前系统的 MessageConverter,看谁支持操作当前这个要发送的对象

  • 找到支持操作对象的converter,把converter支持的媒体类型统计出来
    在这里插入图片描述

  • 进行内容协商的最佳匹配媒体类型

  • 用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化

小结:

  1. @ResponseBody 响应数据出去 调用 RequestResponseBodyMethodProcessor 处理
  2. Processor 处理方法返回值。通过 MessageConverter 处理
  3. 所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)
  4. 内容协商找到最终的 messageConverter;

2.3 自定义Converter

场景一:通过浏览器请求头确定自定义返回数据的格式(Accept字段)
/**
 * @program: SpringDemo
 * @description: 自定义的消息转换器
 * @create: 2021-01-08 09:14
 **/
public class MyMessageConverter implements HttpMessageConverter<User> {
    
    

    /***
     * @Description: 服务器需要统计各个Converter能读哪些内容的数据
     * @Param: [aClass, mediaType]
     * @return: boolean
     * @Date: 2021/1/8
     */
    @Override
    public boolean canRead(Class aClass, MediaType mediaType) {
    
    
        return false;
    }
    
    /***
    * @Description: 服务器需要统计各个Converter能写哪些内容的数据
    * @Param: [aClass, mediaType]
    * @return: boolean
    * @Date: 2021/1/8
    */ 
    @Override
    public boolean canWrite(Class aClass, MediaType mediaType) {
    
    
        return aClass.isAssignableFrom(User.class);
    }

    /***
     * @Description: 表明本转换器支持的类型
     * @Param: [aClass, mediaType]
     * @return: boolean
     * @Date: 2021/1/8
     */
    @Override
    public List<MediaType> getSupportedMediaTypes() {
    
    
        return MediaType.parseMediaTypes("application/x-mypro");
    }

    @Override
    public User read(Class<? extends User> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
    
    
        return null;
    }

    @Override
    public void write(User user, MediaType mediaType, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
    
    
        //自定义格式数据的写出
        String data = user.getName()+";"+user.getName();
        OutputStream body = httpOutputMessage.getBody();
        body.write(data.getBytes());
    }
    
}
@Configuration(proxyBeanMethods = true)
public class MyConfig {
    
    

	//在配置类中添加自定义的Converter
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
    
    
        return new WebMvcConfigurer() {
    
    
            @Override
            public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    
    
                converters.add(new MyMessageConverter());
            }
        };
    }
    
}
场景二:通过浏览器请求的url确定自定义返回数据的格式(format参数)

如:localhost:8080/test/user?format=json
(此中方式需要在配置文件中手动开启)

	@Bean
    public WebMvcConfigurer webMvcConfigurer(){
    
    
        return new WebMvcConfigurer() {
    
    

            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    
    
                HashMap<String, MediaType> map = new HashMap<>();
                map.put("json",MediaType.APPLICATION_JSON);
                map.put("xml",MediaType.APPLICATION_XML);
                map.put("pro",MediaType.parseMediaType("application/x-mypro"));
                //添加媒体协商
                //指定支持解析哪些参数对应的媒体类型
                ParameterContentNegotiationStrategy parameterContentNegotiationStrategy = new ParameterContentNegotiationStrategy(map);
                
                //添加请求头协商
                HeaderContentNegotiationStrategy headerContentNegotiationStrategy = new HeaderContentNegotiationStrategy();
                configurer.strategies(Arrays.asList(parameterContentNegotiationStrategy,headerContentNegotiationStrategy));
                
                
            }

            @Override
            public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    
    
                converters.add(new MyMessageConverter());
            }
        };

    }

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42451178/article/details/112308749