帶有請求正文的 HTTP 刪除
一、簡介
在本快速教程中,我們將實作一個接受正文的 HTTP DELETE 端點,然後探索向其發送請求的多種方法。因此,我們將使用不同的流行 REST 客戶端實作。
2. 問題
HTTP 規範對於DE
請求是否可以包含主體的問題不明確,指出「DELETE 請求中接收到的ETE
content received in a DELETE request has no generally defined semantics.
” 這讓實作來定義行為。雖然從技術上講可以在 DELETE 請求中包含正文,但不保證伺服器會接受或處理它。
我們將研究 Spring 的RestController
如何接受和處理 DELETE 請求中的@RequestBody
。為簡單起見,我們將建立一個/delete
端點來回顯請求正文,使我們能夠驗證正文是否已正確處理:
@RestController
@RequestMapping("/delete")
public class DeleteController {
@DeleteMapping
public Body delete(@RequestBody Body body) {
return body;
}
}
我們的body
是一個簡單的 POJO:
public class Body {
private String name;
private Integer value;
// standard getters and setters
}
在測試期間,我們將在請求中使用簡單的 JSON String
,以便我們可以輕鬆匹配返回的內容,而無需額外解析:
String json = "{\"name\":\"foo\",\"value\":1}"
我們現在準備好探索現有的 REST 客戶端實現,這些實作允許我們在請求中發送內容。
3.使用Spring的RestTemplate
我們的第一個選擇是使用 Spring 中流行的RestTemplate
。我們將編寫一個客戶端類,在其建構函數中接收 URL:
public class SpringTemplateDeleteClient {
private final String url;
private RestTemplate client = new RestTemplate();
public SpringTemplateDeleteClient(String url) {
this.url = url;
}
// ...
}
由於RestTemplate
不提供接受正文的重載delete()
方法,因此我們將更通用的exchange()
方法與HttpMethod.DELETE
一起使用。讓我們看看它是什麼樣子的:
public String delete(String json) {
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
HttpEntity<String> body = new HttpEntity<>(json, headers);
ResponseEntity<String> response = client.exchange(
url, HttpMethod.DELETE, body, String.class);
return response.getBody();
}
由於我們的控制器傳回的正文與收到的完全完全相同,因此我們可以斷言它正確處理了 DELETE 請求中的正文:
@Test
void whenRestTemplate_thenDeleteWithBody() {
SpringTemplateDeleteClient client = new SpringTemplateDeleteClient(url);
assertEquals(json, client.delete(json));
}
4. 使用核心 Java 類
Java 11中的HttpClient
缺少支援主體的專用delete()
方法,因此我們在HttpRequest.newBuilder()中使用通用method(“DELETE”)
HttpRequest.newBuilder().
讓我們建立一個具有相同整體模板的客戶端,一個使用包含delete()
方法的 URL 建構的類別。我們將收到一個 JSON String
並使用該方法建構一個BodyPublisher
。最終,我們將以String
形式傳回回應正文:
public class PlainDeleteClient {
private final String url;
private HttpClient client = HttpClient.newHttpClient();
// url constructor
public String delete(String json) throws Exception {
BodyPublisher body = HttpRequest.BodyPublishers.ofString(json);
// ...
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());
return response.body();
}
}
對於實際請求,我們將使用HttpRequest.newBuilder()
,它不包含接受正文的delete()
幫助器。相反,我們將使用通用method(“DELETE”)
:
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.header("Content-Type", "application/json")
.method("DELETE", body)
.build();
我們來測試一下:
@Test
void whenPlainHttpClient_thenDeleteWithBody() throws Exception {
PlainDeleteClient client = new PlainDeleteClient(url);
assertEquals(json, client.delete(json));
}
5. 使用 Apache HTTP 4
一個流行的選擇是使用 Apache HTTP 客戶端。有了這個函式庫,像HttpPost
這樣的標準實作就擴展了HttpEntityEnclosingRequestBase
,其中包含setEntity()
方法,允許我們在請求中包含主體。
不幸的是, HttpDelete
僅擴展了HttpRequestBase
,它不包含定義請求實體的方法。因此,我們首先擴展HttpEntityEnclosingRequestBase
以建立自訂HttpDeleteBody
,它會傳回DELETE
作為 HTTP 方法。我們還將包含一個接受String
URL 的建構子:
public class HttpDeleteBody extends HttpEntityEnclosingRequestBase {
public HttpDeleteBody(final String uri) {
super();
setURI(URI.create(uri));
}
@Override
public String getMethod() {
return "DELETE";
}
}
然後,我們可以在客戶端中實例化它並呼叫setEntity()
來傳遞我們的主體。最後,我們將它與client.execute()
方法一起使用:
public class ApacheDeleteClient {
// url constructor
public String delete(String json) throws IOException {
try (CloseableHttpClient client = HttpClients.createDefault()) {
StringEntity body = new StringEntity(json, ContentType.APPLICATION_JSON);
HttpDeleteBody delete = new HttpDeleteBody(url);
delete.setEntity(body);
CloseableHttpResponse response = client.execute(delete);
return EntityUtils.toString(response.getEntity());
}
}
}
顧名思義, CloseableHttpClient
實作Closeable
,因此我們在 try-with-resources 區塊中建立它。該框架還包括一個EntityUtils
類別來幫助我們將回應實體轉換為所需的類型。
5.1.使用 Apache HTTP 5
在該程式庫的版本 5 中,預設的HttpDelete
實作已包含定義請求正文所需的所有內容。另外,執行請求現在是異步的,因此我們必須建立一個HttpClientResponseHandler
。讓我們將其替換為先前的範例,看看它是什麼樣子:
HttpDelete delete = new HttpDelete(url);
delete.setEntity(body);
HttpClientResponseHandler handler = response -> {
try (HttpEntity entity = response.getEntity()) {
return EntityUtils.toString(entity);
}
};
return client.execute(delete, handler);
最後, HttpEntity
現在也實作了Closeable
,因此我們必須使用 try-with-resources 來宣告它。
六、結論
在本文中,我們探討了一些能夠傳送帶有正文的 DELETE HTTP 請求的客戶端實作。每種方法都有優點,選擇取決於我們的特定要求,例如依賴性偏好。
與往常一樣,原始碼可以在 GitHub 上取得。