几个星期以来,我一直在使用spring-mvc进行休息api。 REST-API工作正常,在涉及使用特定错误对象的错误处理时,我几乎完成了直到最后一个问题。
REST-API使用JSON作为格式来序列化Java对象。在服务执行期间发生错误时,会创建特定的错误对象并将其发送回客户端。
当我的休息服务被标记为" produce = application / json"时,一切正常。但是也有一些服务只需要使用" produce = text / plain"返回简单文本。 当其中一个服务发生错误时,Spring-MVC将抛出HttpMediaTypeNotAcceptableException。似乎是正确的,因为客户要求内容类型" text / plain"但服务器响应" application / json"。
你能告诉我这个问题的正确解决方案吗?
仅将JSON用作响应内容类型,并将简单文本包装在特殊的类对象中。 =>在我看来并不像REST那样,因为REST应该支持多种内容类型。
每项服务" text"将被标记为" produce = application / json; text / plain"并且客户还需要发送" accept-header"。 =>当这样做时,API似乎支持相同资源的两种内容类型。但那不对。只有在出现错误的情况下,API才会返回JSON,否则它将始终为" text"。
听起来像是一个非常特殊的REST问题,并且无法找到关于此主题的相关问题。
答案 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"
}