服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Spring Boot数据响应问题实例详解

Spring Boot数据响应问题实例详解

2022-09-20 20:27花粥之间 Java教程

这篇文章主要给大家介绍了关于Spring Boot数据响应问题的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

前言

响应页面指的是我们如何发送一个请求,跳转到指定页面。将会在后面的视图解析中说明。 响应页面常见于开发单体应用。 响应数据常见于开发前后端分离的应用。后端代码主要用来接收请求。前端页面给我们发送过来请求,给前端响应json数据。或者给前端响应xml、图片、音视频数据。

在前后端分离开发过程中,后端一般会将数据集封装成一个JSON对象响应给前端 ,一般只需要标准ResponseBody即可给前端返回数据

 

1、响应Json数据:Jackson.jar+@ResponseBody

假设给前端自动返回json数据,需要引入相关的依赖

<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>

控制层代码如下:引入了依赖后,给方法上标注@ResponseBody,就可以给前端自动返回JSON数据。

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

}

测试:

Spring Boot数据响应问题实例详解

 

2、原理解析

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

SpringMVC到底支持哪些返回值

  • ModelAndView //包含数据和页面
  • Model
  • View
  • ResponseEntity
  • ResponseBodyEmitter
  • StreamingResponseBody
  • HttpEntity
  • HttpHeaders
  • Callable //异步
  • DeferredResult
  • ListenableFuture
  • CompletionStage
  • WebAsyncTask
  • 有 @ModelAttribute 且为对象类型的
  • @ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;处理器//即在方法上或者类上是否标注@ResponseBody

HTTPMessageConverter原理

MessageConverter规范

Spring Boot数据响应问题实例详解

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

默认的MessageConverter

Spring Boot数据响应问题实例详解

  • 0 - 只支持Byte类型的
  • 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 - 支持注解方式xml处理的。

最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)

 

3、内容协商

3.1、概述

根据客户端接收能力不同【有的只接收xml,有的只接收json】,返回不同媒体类型的数据。比如返回xml数据给前

引入支持XML依赖:

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

重新编译该项目运行 ,返回了xml数据

Spring Boot数据响应问题实例详解

3.2、postman分别测试返回json和xml

在上面的测试中,此时如果我用postman发送相同的请求,则得到的是json数据,为啥同样的请求,方式不一样,返回的数据不一样呢。原因就是请求头中规定的数据响应先后顺序

查看请求头

Spring Boot数据响应问题实例详解

内容协商Accept中,浏览器具备什么类型数据的接收能力,可以看到xml数据是优先被接收的。

可用Postman软件分别测试返回json和xml:只需要改变请求头中Accept字段(application/json、application/xml)。Http协议中规定的,告诉服务器本客户端可以接收的数据类型

Spring Boot数据响应问题实例详解

3.3、开启浏览器参数方式内容协商功能

为了方便内容协商,开启基于请求参数的内容协商功能。

spring:
  contentnegotiation:
    favor-parameter: true  #开启请求参数内容协商模式

发请求:

  • json类型: http://localhost:8080/test/person?format=json
  • xml类型:http://localhost:8080/test/person?format=xml

确定客户端接收什么样的内容类型;

1、Parameter策略优先确定是要返回json数据(获取请求头中的format的值) 2、最终进行内容协商返回给客户端json即可。

 

4、内容协商原理

  • 判断当前响应头中是否已经有确定的媒体类型。MediaType
  • 获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】
  • contentNegotiationManager 内容协商管理器 默认使用基于请求头的策略
  • HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型
  • 遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person)
  • 找到支持操作Person的converter,把converter支持的媒体类型统计出来。
  • 客户端需要【application/xml】。服务端能力【10种、json、xml】
  • 进行内容协商的最佳匹配媒体类型
  • 用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。

导入了jackson处理xml的包,xml的converter就会自动进来

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()));
          }

 

5、自定义消息转换器MessageConverter

5.1、概述

实现多协议数据兼容。json、xml、x-guigu

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

要自定义SpringMVC的什么功能,即通过一个入口给容器中添加一个 WebMvcConfigurer

假设你想基于自定义请求参数的自定义内容协商功能。换句话,在地址栏输入http://localhost:8080/test/person?format=gg返回数据,跟http://localhost:8080/test/person且请求头参数`Accept:application/x-guigu`的返回自定义协议数据的一致。

演示

通过上文分析,我们只需要实现WebMvcConfigurer接口,并实现了configureMessageConverters方法,就可以达到自定义消息转换器的目的。例如,我不想用jackson了,想用fastjson的消息转换器,我们可以添加fastjson相关的MessageConverter就可以了

@Configuration
public class WebConfig implements WebMvcConfigurer {

  @Override
  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
      FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
      List<MediaType> fastMediaTypes = new ArrayList<>();
      fastMediaTypes.add(MediaType.TEXT_HTML);
      fastMediaTypes.add(MediaType.APPLICATION_JSON);
      fastConverter.setSupportedMediaTypes(fastMediaTypes);
      FastJsonConfig fastJsonConfig = new FastJsonConfig();
      fastJsonConfig.setSerializerFeatures(
          SerializerFeature.WriteMapNullValue,
          SerializerFeature.WriteNullStringAsEmpty,
          SerializerFeature.WriteNullListAsEmpty,
          SerializerFeature.WriteDateUseDateFormat);
      SerializeConfig serializeConfig = SerializeConfig.globalInstance;
      serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
      serializeConfig.put(Long.class, ToStringSerializer.instance);
      serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
      fastJsonConfig.setSerializeConfig(serializeConfig);
      fastConverter.setFastJsonConfig(fastJsonConfig);
      converters.add(fastConverter);
  }
}

测试

@Data
public class Person {
  private String userName;
  private Integer age;
  //使用fastjson的注解进行转换
  @JSONField(format = "yyyy-MM-dd")
  private Date birth;
  private Pet pet;
}

Spring Boot数据响应问题实例详解

5.2、自定义的Converter

除此之外,这些都是默认的,我们可以进行扩展,如下实现自定义的设置转化,如下,利用这个代码:

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

      @Override
      public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

      }
  }
}

测试

@Configuration(proxyBeanMethods = false)
public class WebConfig {
  @Bean
  public WebMvcConfigurer webMvcConfigurer(){
      return new WebMvcConfigurer() {

          @Override
          public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
              converters.add(new GuiguMessageConverter());
          }
      }
  }
}
/**
* 自定义的Converter
*/
public class GuiguMessageConverter 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都能写出哪些内容类型
   *
   * application/x-guigu
   * @return
   */
  @Override
  public List<MediaType> getSupportedMediaTypes() {
      return MediaType.parseMediaTypes("application/x-guigu");
  }

  @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());
  }
}

测试:

import java.util.Date;

@Controller
public class ResponseTestController {

  /**
   * 1、浏览器发请求直接返回 xml    [application/xml]        jacksonXmlConverter
   * 2、如果是ajax请求 返回 json   [application/json]      jacksonJsonConverter
   * 3、如果硅谷app发请求,返回自定义协议数据  [appliaction/x-guigu]   xxxxConverter
   *          属性值1;属性值2;
   *
   * 步骤:
   * 1、添加自定义的MessageConverter进系统底层
   * 2、系统底层就会统计出所有MessageConverter能操作哪些类型
   * 3、客户端内容协商 [guigu--->guigu]
   *
   * 作业:如何以参数的方式进行内容协商
   * @return
   */
  @ResponseBody  //利用返回值处理器里面的消息转换器进行处理
  @GetMapping(value = "/test/person")
  public Person getPerson(){
      Person person = new Person();
      person.setAge(28);
      person.setBirth(new Date());
      person.setUserName("zhangsan");
      return person;
  }
}

日后开发要注意,有可能我们添加的自定义的功能会覆盖默认很多功能,导致一些默认的功能失效。

 

总结

到此这篇关于Spring Boot数据响应问题的文章就介绍到这了,更多相关SpringBoot数据响应内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://juejin.cn/post/7073435290546733069

延伸 · 阅读

精彩推荐