[英]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”。
你能告訴我這個問題的正確解決方案是什么嗎?
僅使用 JSON 作為響應內容類型並始終將簡單文本包裝在特殊的類對象中。 => 在我看來並不是真正的 REST,因為 REST 應該支持多種內容類型。
每個服務“文本”的服務都將被標記為“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.