Spring中的HTTP请求与响应实体(以及 entity 与 body 的区别)

0、基本概念

报文(message):

HTTP的一个请求或响应叫做报文(message),是HTTP通信的基本单位,分为请求报文(request message)和响应报文(response message)两类。

报文由起始行(start line)、首部(header)和可选的主体(body)三部分(其实还包含header之后、body之前的空行CRLF。即使没有header或body,也应该有一个CRLF)。

首部(header):

首部分为通用首部、请求首部、响应首部、实体首部、扩展首部5类。

在Spring中,表示header的类为HttpHeaders,HttpHeaders是MultiValueMap<String, String>的子类,说明一个header可以有多个值。

主体(body):

主体是HTTP报文的载荷(payload),即HTTP要传输的内容。它包含实际的数据,对于一些请求(如GET请求)或响应可能不存在body。

主体又称为报文主体(message body)或实体主体(entity body),在Spring中用泛型表示,说明可以为任意类型。在没有传输编码时, 报文主体等于实体主体。

 

1、实体(entity)

实体包括实体首部(entity header)和实体主体(entity body)。entity的存在依赖于body,如果没有body,就没有了entity。

通常情况下,即在没有传输编码时,实体只有实体主体,报文主体等于实体主体。只有当传输中进行编码操作时,实体主体的内容发生变化,才导致它和报文主体产生差异。在现在的HTTP协议下,传输编码只有“Transfer-Encoding: chunked”这一种。

实体首部类型

实体首部是指那些用来描述实体主体内容的首部,它告知报文接收者body的一些信息。实体首部包括3类:

- 信息性首部:Allow(可对此body执行的请求方法),Location(即重定向中指出资源的位置)

- 内容首部:说明内容的类型、size以及其他信息,比如很多以Content开头的首部(Content-Type、Content-Length、Content-Encoding)。

- 实体缓存首部:说明如何或什么时候进行缓存,比如ETag、Expires、Last-Modified。

实体首部的位置 

当报文body没有进行编码时,实体首部就位于报文首部中,是报文首部的一部分;

当报文body进行了编码,则报文首部和报文body中都会有实体首部。如下例子中,在传输中需要对body进行编码操作,以便传输表单内容:

POST /upload HTTP/1.1
Host: example.com
Content-Length: xxx
Content-Type: multipart/form-data; boundary=AaBbCcDd

--AaBbCcDd
Content-Disposition: form-data; name="username"

RuphiLau
--AaBbCcDd
Content-Disposition: form-data; name="file"; filename="picture.jpg"
Content-Type: image/jpeg

...(picture.jpg的数据)...
--AaBbCcDd--

在这个报文的首部和body中都有实体首部,而且该报文有多个实体(表单中的每段内容为一个实体),每个实体里有实体头部、实体主体,并过CR+LF分割。

entity被payload取代

另外,定义 entity 概念的RFC 2616,目前已经被 RFC 7230 到 7235 取代,术语实体(entity)被有效载荷(payload)代替。

报文(message)和有效载荷(payload)的区别比较明显,另一个容易混淆的点是 message body 和 payload body。

根据 RFC 7230:HTTP 报文的报文主体(message body)(如果存在的话)是用来运载请求或响应的有效载荷主体(payload body)的。除非应用了传输编码,报文主体等价于有效载荷主体。

以分块传输编码(Chunked transfer encoding)的一个示例来解释message body与payload body的区别:

HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

25
This is the data in the first chunk

1C
and this is the second one

3
con

8
sequence

0

示例中的payload body为 This is the data in the first chunk、and this is the second one、con 和 sequence 这几行。而message body为第一个空行以后的所有部分,除了有效载荷主体之外,还包括了 25、1C 等行和几个空行。

Spring中的实体 

在Spring中,有一个HttpEntity类,就表示HTTP请求实体,它有两个子类RequestEntityResponseEntity,分别表示请求实体和响应实体。

不论是请求实体还是响应实体,entity 都包含首部(header)和主体(body)(对于GET请求,body可能为空)。

 

2、RequestEntity 

RequestEntity 的使用

RequestEntity 是用于发起HTTP请求的实体,相对于HttpEntity,它增加HTTP请求方法、URL。

RequestEntity 可以用在发起HTTP请求的客户端代码中,也可以用在处理HTTP请求的服务端代码中。

1)HTTP客户端使用:在 RestTemplate 的 exchange() 方法中使用

MyRequest body = ...
RequestEntity<MyRequest> request = RequestEntity
    .post(new URI("https://example.com/bar"))
    .accept(MediaType.APPLICATION_JSON)
    .body(body);
ResponseEntity<MyResponse> response = template.exchange(request, MyResponse.class);

2)HTTP服务端使用

在Spring MVC中,RequestEntity 可以作为 Controller 方法的入参:

@RequestMapping("/handle")
public void handle(RequestEntity<String> request) {
  HttpMethod method = request.getMethod();
  URI url = request.getUrl();
  String body = request.getBody();
}

 

RequestEntity 实例的创建

RequestEntity 可以通过普通的构造方法进行实例化,其参数最全的构造方法为:

public RequestEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers,
            @Nullable HttpMethod method, URI url, @Nullable Type type)

 

RequestEntity 也可以通过Builder进行实例化。如 RequestEntity  的静态方法 get(URI url)、post(URI url) 等将会返回一个 HeadersBuilder<?> 或 BodyBuilder 实例,该实例包含了header信息,可以进一步设置其他的header。

然后调用该 Builder 的 body()(有body情况下用于指定body) 或 build() 方法,创建指定的RequestEntity实例。

// Create shared factory
UriBuilderFactory factory = new DefaultUriBuilderFactory();

// Use factory to create URL from template
URI uri = factory.uriString("https://example.com/{foo}").build("bar");
RequestEntity<MyRequest> request = RequestEntity.post(uri).accept(MediaType.APPLICATION_JSON).body(body);

 

3、ResponseEntity

ResponseEntity 的使用

ResponseEntity 是HTTP响应实体,相对于父类HttpEntity,它增加了响应状态码。

状态码用类HttpStatus表示,它包含了状态值(value,如200)和原因短语(reason phrase,如OK)。

ResponseEntity 可以在HTTP客户端请求数据时使用,也可以在HTTP服务端处理请求时使用。

1)HTTP客户端:通过 RestTemplate getForEntity()exchange() 方法请求数据时返回ResponseEntity

ResponseEntity<String> entity = template.getForEntity("https://example.com", String.class);
String body = entity.getBody();
MediaType contentType = entity.getHeaders().getContentType();
HttpStatus statusCode = entity.getStatusCode();

2)HTTP服务端:在Spring的 Controller 方法中使用 ResponseEntity

@RequestMapping("/handle")
public ResponseEntity<String> handle() {
    URI location = ...;
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.setLocation(location);
    responseHeaders.set("MyResponseHeader", "MyValue");
    return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
}

 

ResponseEntity 实例的创建 

与 RequestEntity 实例的创建类似,ResponseEntity 实例既可通过普通的构造方法创建;

也可先通过对应某个响应状态的静态方法(如ok()、created(URI location)、status(int status)等),创建相应的BodyBuilder,然后进一步指定header或body,最后build出ResponseEntity实例。

 

参考文档

 

posted @ 2020-05-15 17:56  i江湖中人  阅读(7475)  评论(1编辑  收藏  举报