繁体   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