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實例。

 

參考文檔

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM