几个星期以来,我正在使用spring-mvc开发一个rest api。REST-API 工作正常,我几乎完成了,直到涉及特定错误对象的错误处理的最后一个问题。
REST-API 使用 JSON 作为格式来序列化 Java 对象。在服务执行期间发生错误时,将创建特定的错误对象并将其发送回客户端。
当我的休息服务标记为"produces=application/json"时,一切正常。但也有一些服务只需要使用"produces=text/plain"返回简单文本。当其中一个服务发生错误时,Spring-MVC将抛出一个HttpMediaTypeNotAcceptException。似乎是正确的,因为客户端要求内容类型"文本/纯文本",但服务器响应"应用程序/json"。
你能告诉我这个问题的正确解决方案是什么吗?
-
仅使用 JSON 作为响应内容类型,并将简单文本包装始终包装在特殊的类对象中。 => 在我看来,并不是真正的 REST,因为 REST 应该支持多种内容类型。
-
每个提供"text"的服务都将被标记为"produces=application/json;text/plain",客户端也需要在"accept-header"中发送两者。 => 这样做时,API 似乎支持同一资源的两种内容类型。但这是不对的。只有在出现错误的情况下,API 才会返回 JSON,否则它将始终是"文本"。
对我来说听起来像是一个非常特殊的 REST 问题,找不到有关此主题的相关问题。
我遇到了同样的问题,我对 REST 最佳实践也有完全相同的问题。
我读到的关于处理 API 响应中错误的所有文章都使用 JSON。这里的例子。
我不认为所有这些 API 总是将数据包装在 JSON 中。有时你只需要提供文件,文本或非json的东西......另外,我偶然发现了RFC7807,它提出了一种使用 JSON 格式公开错误/探测的标准方法,即使使用自己的内容类型应用程序/问题+json。因此,我们可以有把握地假设,对 HTTP 200 使用不同的内容类型而不是对 HTTP 错误代码使用是一种很好的做法。
关于如何使用 Spring 框架来做到这一点,它实际上非常简单。一旦你明白了"produce ={}"基本上是一种声明性的方式,表示你的响应将是某种类型,你可以想象也可以以编程方式设置你想要返回的类型。
下面是一个示例 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
}