错误响应中的REST-API不同内容类型

时间:2015-06-17 11:14:11

标签: java json api rest spring-mvc

几个星期以来,我一直在使用spring-mvc进行休息api。 REST-API工作正常,在涉及使用特定错误对象的错误处理时,我几乎完成了直到最后一个问题。

REST-API使用JSON作为格式来序列化Java对象。在服务执行期间发生错误时,会创建特定的错误对象并将其发送回客户端。

当我的休息服务被标记为" produce = application / json"时,一切正常。但是也有一些服务只需要使用" produce = text / plain"返回简单文本。 当其中一个服务发生错误时,Spring-MVC将抛出HttpMediaTypeNotAcceptableException。似乎是正确的,因为客户要求内容类型" text / plain"但服务器响应" application / json"。

你能告诉我这个问题的正确解决方案吗?

  1. 仅将JSON用作响应内容类型,并将简单文本包装在特殊的类对象中。 =>在我看来并不像REST那样,因为REST应该支持多种内容类型。

  2. 每项服务" text"将被标记为" produce = application / json; text / plain"并且客户还需要发送" accept-header"。 =>当这样做时,API似乎支持相同资源的两种内容类型。但那不对。只有在出现错误的情况下,API才会返回JSON,否则它将始终为" text"。

  3. 听起来像是一个非常特殊的REST问题,并且无法找到关于此主题的相关问题。

2 个答案:

答案 0 :(得分:3)

用户应始终使用Accept标头指定其期望的内容。您可以使用Accept标头中指定的格式返回在服务器端抛出/捕获的错误。在春天,据我所知,它可以通过一个特殊的映射器来实现。下面你可以找到这样用groovy编写的mapper来处理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
}

答案 1 :(得分:0)

我面临着同样的问题,关于REST最佳实践我也有完全相同的问题。

我阅读的所有有关处理API响应中的错误的文章都使用JSON。示例here

我认为所有这些API都不总是将数据包装在JSON中。有时您只需要提供文件,文本或非json内容... 另外,我偶然发现了RFC7807,它提出了一种使用JSON格式公开错误/问题的标准方法,即使使用自己的内容类型application / problem + json也是如此。 因此,我们可以放心地假设,对HTTP 200使用与HTTP错误代码不同的内容类型是一种很好的做法。

关于如何使用Spring Framework做到这一点,实际上非常简单。一旦了解了“ produces = {}”基本上是一种声明性的方式来表示您的响应将是某种类型,那么您可以想象,还可以通过编程方式设置要返回的类型。

这是一个示例API,应返回application / octet-stream(二进制文件)。

@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))));
}

工作时,它将返回具有正确内容类型的文件。 现在,如果要处理错误情况(在这种情况下,错误的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"
}