HTTP DELETE 請求體

REST,Spring Web
Remote
1
02:59 AM · Dec 01 ,2025

1. 引言

在本快速教程中,我們將實現一個 HTTP DELETE 端點,該端點接受請求體,然後探索多種方法將請求發送到該端點。 此外,我們還將使用不同的流行 REST 客户端實現。

2. 問題

RFC 9110 規範對 DELETE 請求是否可以包含請求體存在歧義,指出“在 DELETE 請求中接收到的內容沒有明確的語義定義”。 這使得實現者可以定義行為。 雖然在 DELETE 請求中包含請求體在技術上是可能的,但服務器並不能保證接受或處理它。

我們將研究 Spring 的 RestController 如何接受和處理 @RequestBody 在 DELETE 請求中的內容。 為了簡化,我們將創建一個 /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 不提供接受 body 的重載 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();
}

由於我們的控制器返回的 body 與接收到的 body 相同,因此我們可以斷言它正確處理了 DELETE 請求中的 body:

@Test
void whenRestTemplate_thenDeleteWithBody() {
    SpringTemplateDeleteClient client = new SpringTemplateDeleteClient(url);

    assertEquals(json, client.delete(json));
}

4. 使用核心Java類

在Java 11中,HttpClient 缺少支持Body的專用delete() 方法,因此我們使用泛型method(“DELETE”)HttpRequest.newBuilder().中使用。

讓我們創建一個與相同模板相同的客户端,一個構造體,包含一個URL,其中包含一個delete()方法。 我們將接收一個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(),它不包含接受Body的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() 方法,允許我們在請求中包含一個 body。

不幸的是,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() 方法來傳遞我們的 body。 最後,我們使用它與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 實現已經包含我們定義請求 body 所需的一切。 執行請求現在是異步的,因此我們必須構建一個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 聲明它。

6. 結論

在本文中,我們探討了幾種能夠發送帶有請求體的 DELETE HTTP 請求的客户端實現方案。每種方案都有其優勢,選擇取決於我們的具體需求,例如依賴偏好。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.