簡體   English   中英

REST-API 錯誤響應的不同內容類型

[英]REST-API Different Content-Type on Error Response

幾個星期以來,我正在使用 spring-mvc 開發休息 api。 REST-API 工作正常,我幾乎完成了,直到最后一個問題涉及到特定錯誤對象的錯誤處理。

REST-API 使用 JSON 作為格式來序列化 Java 對象。 當服務執行期間發生錯誤時,會創建一個特定的錯誤對象並將其發送回客戶端。

當我的休息服務被標記為“produces=application/json”時,一切正常。 但是也有一些服務只需要使用“produces=text/plain”返回簡單的文本。 當這些服務之一發生錯誤時,Spring-MVC 將拋出一個 HttpMediaTypeNotAcceptableException。 似乎是正確的,因為客戶端要求內容類型為“text/plain”,但服務器響應為“application/json”。

你能告訴我這個問題的正確解決方案是什么嗎?

  1. 僅使用 JSON 作為響應內容類型並始終將簡單文本包裝在特殊的類對象中。 => 在我看來並不是真正的 REST,因為 REST 應該支持多種內容類型。

  2. 每個服務“文本”的服務都將被標記為“produces=application/json;text/plain”,並且客戶端也需要在“accept-header”中發送兩者。 => 這樣做時,API 似乎支持同一資源的兩種內容類型。 但那是不對的。 僅在出現錯誤的情況下,API 才會返回 JSON,否則它將始終為“文本”。

對我來說聽起來像是一個非常特殊的 REST 問題,但找不到與此主題相關的問題。

我面臨着同樣的問題,我對 REST 最佳實踐也有完全相同的問題。

我讀到的所有關於處理 API 響應中的錯誤的文章都使用 JSON。 示例在這里

我不認為所有這些 API 總是將數據包裝在 JSON 中。 有時你只需要提供文件、文本或非 json 內容......此外,我偶然發現了RFC7807 ,它提出了一種標准方法來公開帶有 JSON 格式的錯誤/問題,甚至使用它自己的內容類型應用程序/問題+json。 因此,我們可以安全地假設對 HTTP 200 使用不同的內容類型而不是 HTTP 錯誤代碼是一個很好的做法。

關於如何用Spring Framework來做,其實很簡單。 一旦您理解了“produces ={}”基本上是一種聲明性方式來表示您的響應將是某種類型,您可以想象也可以通過編程方式設置您想要返回的類型。

這是一個應返回 application/octet-stream(二進制文件)的示例 API。

@GetMapping(path = "/1/resources/hello", produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE})
public ResponseEntity<StreamingResponseBody> getFile(@RequestParam(value = "charset", required = false, defaultValue = "UTF-8") String charset) {
    return ResponseEntity.ok().body(outputStream -> outputStream.write("Hello there".getBytes(Charset.forName(charset))));
}

當它工作時,它將返回一個具有正確內容類型的文件。 現在,如果您想處理錯誤情況(在這種情況下是錯誤的字符集參數),您可以創建一個異常處理程序:

@ExceptionHandler(UnsupportedCharsetException.class)
public ResponseEntity<?> handleCharsetException(UnsupportedCharsetException e) {
    return ResponseEntity.badRequest().contentType(MediaType.APPLICATION_JSON_UTF8).body(new ErrorResponse("1", "Wrong charset"));
}

現在,錯誤情況也按預期工作:

GET http://localhost/1/resources/hello?charset=CRAP

HTTP/1.1 400 Bad Request
Connection: keep-alive
Transfer-Encoding: chunked
Content-Type: application/json;charset=UTF-8
Date: Mon, 25 Mar 2019 17:37:39 GMT

{
  "code": "1",
  "message": "Wrong charset"
}

用戶應始終使用Accept標頭指定期望的內容。 您的工作是以Accept標頭中指定的格式返回在服務器端拋出/捕獲的錯誤。 據我所知,春天可以使用特殊的映射器來實現。 您可以在下面找到用 groovy 編寫的此類映射器來處理text/html

import groovy.xml.MarkupBuilder
import org.springframework.http.HttpInputMessage
import org.springframework.http.HttpOutputMessage
import org.springframework.http.converter.AbstractHttpMessageConverter

import static org.springframework.http.MediaType.TEXT_HTML

class ExceptionResponseHTMLConverter extends AbstractHttpMessageConverter<ExceptionResponse> {
  ExceptionResponseHTMLConverter() {
    super(TEXT_HTML)
  }

  @Override
  boolean supports(Class clazz) {
    clazz.equals(ExceptionResponse)
  }

  @Override
  ExceptionResponse readInternal(Class clazz, HttpInputMessage msg) {
    throw new UnsupportedOperationException()
  }

  @Override
  void writeInternal(ExceptionResponse e, HttpOutputMessage msg) {
    def sw = new StringWriter()
    new MarkupBuilder(sw).error {
      error(e.error)
      exception(e.exception)
      message(e.message)
      path(e.path)
      status(e.status)
      timestamp(e.timestamp)
    }
    msg.body << sw.toString().bytes
  }
}

ExceptionResponse類:

class ExceptionResponse {
  String error
  String exception
  String message
  String path
  Integer status
  Long timestamp
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM