08、SpringMVC进阶:HttpMessageConverter之使用分析

HTTP报文

HTTP请求流程

HTTP超文本传输协议(HyperText Transfer Protocol)是一种用于分布式、协作式和超媒体信息系统的应用层协议。

HTTP是基于客户/服务器模式,且面向连接的。典型的HTTP事务处理有如下的过程:
(1)客户与服务器建立连接;
(2)客户向服务器提出请求;
(3)服务器接受请求,并根据请求返回相应的文件作为应答;
(4)客户与服务器关闭连接。

*

HTTP报文

用于HTTP协议交互的信息被称为HTTP报文。请求端的HTTP报文叫请求报文,响应端的叫响应报文。

请求报文

HTTP 请求报文由请求行、请求头部、空行 和 请求包体 4 个部分组成。

*

请求行:请求行由请求方法字段、URL 字段 和HTTP 协议版本字段 3 个部分组成,他们之间使用空格隔开。

请求头部:请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息。

空行:最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头;

报文体:报文主体和首部字段之间通过一个空行分隔,请求主体中包含了要发送给 Web 服务器的数据(一般 POST 请求都会包含请求主体,GET 请求参数都在 URL 里面,请求主体一般为空),响应主体中包含了服务器返回给客户端的数据,一般是 HTML 文档或者 JSON 格式数据。

响应报文

HTTP 响应报文由状态行、响应头部、空行 和 报文体 4 个部分组成。
*

状态行:状态行由 HTTP 协议版本字段、状态码和状态码的描述文本 3 个部分组成,他们之间使用空格隔开;

响应头部:响应头相关信息;

空行:最后一个响应头部之后是一个空行,发送回车符和换行符,通知服务器以下不再有响应头部。

报文体:服务器返回给客户端的信息;

HttpMessageConverter

疑问

在上面我们了解到了HTTP是使用报文的方式进行数据传输的。

在Spring MVC中,我们接收请求参数和响应数据,都是直接使用的对象,那么框架肯定是帮我们处理了报文,包括解析报文为对象,对象解析为报文。
*

converter包

处理HTTP转换相关的代码在spring框架的spring-web模块下的converter包中。

*

HttpMessageConverter接口

HttpMessageConverter就是HTTP报文消息转换器,可以将报文转换为JAVA对象,或者将JAVA对象转换为报文。

它定义了一些读写报文的相关方法。

public interface HttpMessageConverter<T> {
   
     
    boolean canRead(Class<?> var1, @Nullable MediaType var2);

    boolean canWrite(Class<?> var1, @Nullable MediaType var2);

    List<MediaType> getSupportedMediaTypes();

    default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
   
     
        return !this.canRead(clazz, (MediaType)null) && !this.canWrite(clazz, (MediaType)null) ? Collections.emptyList() : this.getSupportedMediaTypes();
    }
	// 从给定的输入消息中读取给定类型的对象,并返回它。
    T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;
	// 将给定的对象写入给定的输出消息。
    void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}

HttpMessageConverter有很多实现接口及子类,支持多种消息转换方式。
*
下面列述几种比较常用的转换器:

转换器 描述
FormHttpMessageConverter 负责读取form提交的数据(能读取的数据格式为 application/x-www-form-urlencoded,不能读取multipart/form-data格式数据);负责写入application/x-www-from-urlencoded和multipart/form-data格式的数据
SourceHttpMessageConverter 负责读取资源文件和写出资源文件数据
ByteArrayHttpMessageConverter 可以读写字节数组
AbstractXmlHttpMessageConverter XML 的转换
ObjectToStringHttpMessageConverter 用于将字符串内容与目标对象类型进行相互转换
ProtobufHttpMessageConverter 读取和写入 com.google.protobuf.Messages使用 谷歌协议转换器
GsonHttpMessageConverter Gson 转换器
MappingJackson2HttpMessageConverter Jackson 2.x 读取和写入 JSON

注解支持

HttpMessageConverter提供了@RequestBody及@ResponseBody注解用于报文体转换。

@RequestBody

@RequestBody可以根据请求的内容类型解析为方法参数。

也就是可以获取请求体,在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值。

GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。

@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据)。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
   
     
	boolean required() default true;
}

案例

1、 编写一个控制器方法,Post请求,形参添加注解@RequestBody,表示将请求报文转为User对象;

    @PostMapping("/testRuquestBody")
    public Object testRuquestBody(@RequestBody User user) {
   
     
        return user;
    }

1、 发送Post请求,这里注意,需要传递application/json格式的数据,并绑定到请求体中;

*

@ResponseBody

@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器。

@ResponseBody注解的作用是将controller的方法返回的对象通过转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。

注意:在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。

从4.0 版本开始,这个注解也可以在类型级别添加,在这种情况下它是继承的,不需要在方法级别添加。

案例

1、 编写一个@Controller控制器方法,不添加@ResponseBody注解时,发现,MVC会走视图处理器,认为这个控制器返回的是视图名称;
*
2、 添加了@ResponseBody注解后,字符串直接被解析为报文响应给了浏览器;
*

@RestController

@RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
   
     

	@AliasFor(annotation = Controller.class)
	String value() default "";

}

相关类支持

Spring MVC也提供了一些类,可以用来表示报文实体

HttpEntity

HttpEntity类表示一个 HTTP 请求或响应实体,由头部和报文体组成,其泛型T表示报文内容。

	private final HttpHeaders headers;
	@Nullable
	private final T body;

RequestEntity

RequestEntity继承自HttpEntity,主要是添加了 HTTP 方法和 目标 URL属性。

RequestEntity表示封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息。

案例

1、 编写一个@Controller控制器方法,直接在形参设置RequestEntity类型,指定泛型为User;

    @PostMapping("/testRequestEntity")
    public Object testRequestEntity(RequestEntity<User> requestEntity) {
   
     
        HttpMethod method = requestEntity.getMethod();
        URI url = requestEntity.getUrl();
        Type type = requestEntity.getType();
        User body = requestEntity.getBody();
        HttpHeaders headers = requestEntity.getHeaders();
        return body;
    }

1、 可以看到RequestEntity接受了请求报文的相关信息;
*

ResponseEntity

RequestEntity继承自HttpEntity,主要是添加了添加 {@link HttpStatus} 状态代码。

ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文。

案例

1、 编写一个@Controller控制器方法,返回ResponseEntity类型,指定泛型为User;

    @GetMapping("/testResponseEntity")
    public ResponseEntity<User> testResponseEntity() {
   
     
        User user = new User();
        user.setName("吃个桃桃");
        user.setAge(15);
        return new ResponseEntity<>(user, HttpStatus.OK);
    }

1、 测试发现,Body中的数据和HttpStatus直接返回;
*

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: