响应页面指的是我们如何发送一个请求,跳转到指定页面。将会在后面的视图解析中说明。
响应页面常见于开发单体应用。
响应数据常见于开发前后端分离的应用。后端代码主要用来接收请求。前端页面给我们发送过来请求,给前端响应json数据。或者给前端响应xml、图片、音视频数据。
假设给前端自动返回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>
springboot,控制层代码如下
@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;}}
引入了依赖后,给方法上标注@ResponseBody,就可以给前端自动返回JSON数据。
原理:
有讨论ReturnValueHandler。现在直接看看重点:
应用程序没有响应怎么办?1、返回值解析器
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
@Overridepublic 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.返回值解析器原理
ModelAndView //包含数据和页面
Model
View
ResponseEntity
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable //异步
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且为对象类型的
@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;处理器//即在方法上或者类上是否标注@ResponseBody
为响应。
HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。
例子:CanWrite将Person对象转为JSON。canRead或者 JSON转为Person
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转换的)
springboot中文手册、根据客户端接收能力不同,返回不同媒体类型的数据。
<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId>
</dependency>
重新编译运行
查看请求头
内容协商Accept中,浏览器具备什么类型数据的接收能力,可以看到xml数据是优先被接收的。
只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。
无响应、为了方便内容协商,开启基于请求参数的内容协商功能。
spring:contentnegotiation:favor-parameter: true #开启请求参数内容协商模式
发请求: http://localhost:8080/test/person?format=json
http://localhost:8080/test/person?format=xml
确定客户端接收什么样的内容类型;
1、Parameter策略优先确定是要返回json数据(获取请求头中的format的值)
2、最终进行内容协商返回给客户端json即可。
Springboot框架,1、判断当前响应头中是否已经有确定的媒体类型。MediaType
2、获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】
导入了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()));}
实现多协议数据兼容。json、 xml、 x-guigu
0、@ResponseBody 响应数据出去调用RequestResponseBodyMethodProcessor处理
1、Processor处理方法返回值。通过MessageConverter处理
2、所有MessageConverter合起来可以支持各种媒体类型数据的操作(读、写)
3、内容协商找到最终的messageConverter;
SpringMVC的什么功能。一个入口给容器中添加一个WebMvcConfigurer
@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}}}
@Configuration(proxyBeanMethods = false)
public class WebConfig {@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(new GuiguMessageConverter());}}}
}
/*** 自定义的Converter*/
public class GuiguMessageConverter implements HttpMessageConverter<Person> {@Overridepublic boolean canRead(Class<?> clazz, MediaType mediaType) {return false;}@Overridepublic boolean canWrite(Class<?> clazz, MediaType mediaType) {return clazz.isAssignableFrom(Person.class);}/*** 服务器要统计所有MessageConverter都能写出哪些内容类型** application/x-guigu* @return*/@Overridepublic List<MediaType> getSupportedMediaTypes() {return MediaType.parseMediaTypes("application/x-guigu");}@Overridepublic Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {return null;}@Overridepublic 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;}}
用Postman发送/test/person(请求头Accept:application/x-guigu),将返回自定义协议数据的写出。
有可能我们添加的自定义的功能会覆盖默认很多功能,导致-些默认的功能失效。
大家考虑,. 上述功能除了我们完全自定义外? SpringBoot有没有为我们提供基于配置文件的快速修改媒体类型功能?怎么配置呢? [提示: 参照SpringBoot官方文档web开发内容协商章节]
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态