我有一个用@ControllerAdvice
注释的类,其中有这个方法:
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public ExceptionInfo resourceNotFoundHandler(ResourceNotFoundException ex) {
List<ErrorContent> errors = new ArrayList<>();
errors.add(new ErrorContent(ExceptionsCodes.NOT_FOUND_CODE, null,
"test"));
return fillExceptionInfo(HttpStatus.NOT_FOUND, errors, ex);
}
以下是fillExceptionInfo
:
public ExceptionInfo fillExceptionInfo(HttpStatus status, List<ErrorContent> errors,
Exception ex) {
String msg = ex.getMessage();
return new ExceptionInfo(status.toString(), errors, (msg != null && !msg.equals(""))
? ex.getMessage()
: ExceptionUtils.getFullStackTrace(ex));
}
当Web客户端发送一些无法找到的json数据请求时,此方法可以正常工作。但是当服务器收到映像请求时,会抛出HttpMediaTypeNotAcceptableException
而不是异常。我知道它是因为内容类型错误而发生的,但我该如何解决这个问题?
更新
我的目标是在json数据和文件两种情况下抛出ResourceNotFoundException
。
我得到的异常(因此它是从AbstractMessageConverterMethodProcessor
抛出的):
ERROR o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - doResolveHandlerMethodException - Failed to invoke @ExceptionHandler method: public com.lia.utils.GlobalExceptionHandler$ExceptionInfo com.lia.utils.GlobalExceptionHandler.resourceNotFoundHandler(com.lia.app.controllers.exceptions.ResourceNotFoundException)
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:168) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:101) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:198) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71) ~[spring-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:122) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.doResolveHandlerMethodException(ExceptionHandlerExceptionResolver.java:362) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver.doResolveException(AbstractHandlerMethodExceptionResolver.java:60) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:138) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.processHandlerException(DispatcherServlet.java:1167) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1004) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:955) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:687) [javax.servlet-api-3.1.0.jar:3.1.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [javax.servlet-api-3.1.0.jar:3.1.0]
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:717) [jetty-servlet-9.1.1.v20140108.jar:9.1.1.v20140108]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1644) [jetty-servlet-9.1.1.v20140108.jar:9.1.1.v20140108]
....
答案 0 :(得分:8)
问题在于请求的内容类型与返回的对象不兼容。请参阅my response,了解如何配置ContentNegotiationConfigurer
,以便Spring根据您的需求确定所请求的内容类型(查看路径扩展名,URL参数或Accept
标题)。
根据确定请求的内容类型的方式,客户端请求图像时,您有以下选项:
Accept
标头确定,并且客户端可以/想要处理JSON响应而不是图像数据,则客户端应使用Accept: image/*, application/json
发送请求。这样Spring就可以安全地返回图像字节数据或错误JSON消息。直接在响应中设置错误代码
public byte[] getImage(HttpServletResponse resp) {
try {
// return your image
} catch (Exception e) {
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
使用ResponseEntity
public ResponseEntity<?> getImage(HttpServletResponse resp) {
try {
byte[] img = // your image
return ReponseEntity.ok(img);
} catch (Exception e) {
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
在该控制器中使用单独的@ExceptionHandler
方法 ,这将覆盖默认的Spring异常处理。假设您有图像请求的专用异常类型或仅用于提供图像的单独控制器。否则,异常处理程序也将处理该控制器中其他端点的异常。
答案 1 :(得分:3)
您的ExceptionInfo
课程是什么样的?在@ControllerAdvice
带注释的类中定义了一些异常处理程序后,我遇到了类似的问题。当异常发生时,虽然没有返回并且org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
被抛出,但它被捕获了。
我发现问题是由于我错过了将getter方法添加到我的ErrorResponse
类这一事实引起的。添加getter方法之后(这个类是不可变的,所以没有setter方法)所有东西都像魅力一样。
答案 2 :(得分:0)
如果您愿意忽略客户的明确指示(如Accept
标头所示),则可以修改内容协商策略,如下所示:
/**
* Content negotiation strategy that adds the {@code application/json} media type if not present in the "Accept"
* header of the request.
* <p>
* Without this media type, Spring refuses to return errors as {@code application/json}, and thus not return them at
* all, which leads to a HTTP status code 406, Not Acceptable
*/
class EnsureApplicationJsonNegotiationStrategy extends HeaderContentNegotiationStrategy {
@Override
public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
List<MediaType> mediaTypes = new ArrayList<>(super.resolveMediaTypes(request));
if (notIncludesApplicationJson(mediaTypes)) {
mediaTypes.add(MediaType.APPLICATION_JSON);
}
return mediaTypes;
}
private boolean notIncludesApplicationJson(List<MediaType> mediaTypes) {
return mediaTypes.stream()
.noneMatch(mediaType -> mediaType.includes(MediaType.APPLICATION_JSON));
}
}
在@Configuration
类中使用它,如下所示:
@Configuration
public class ContentNegotiationConfiguration implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.strategies(List.of(
new EnsureApplicationJsonNegotiationStrategy()
));
}
}
单元测试(使用JUnit 5,Mockito):
@ExtendWith(MockitoExtension.class)
public class EnsureApplicationJsonNegotiationStrategyTest {
@Mock
private NativeWebRequest request;
@InjectMocks
private EnsureApplicationJsonNegotiationStrategy subject;
@Test
public void testAddsApplicationJsonToAll() throws HttpMediaTypeNotAcceptableException {
when(request.getHeaderValues(HttpHeaders.ACCEPT)).thenReturn(new String[]{"*/*"});
assertThat(subject.resolveMediaTypes(request), contains(
MediaType.ALL // this includes application/json, so... fine
));
}
@Test
public void testAddsApplicationJsonToEmpty() throws HttpMediaTypeNotAcceptableException {
when(request.getHeaderValues(HttpHeaders.ACCEPT)).thenReturn(new String[0]);
assertThat(subject.resolveMediaTypes(request), contains(
MediaType.ALL // that's what the default does, which includes application/json, so... fine
));
}
@Test
public void testAddsApplicationJsonToExisting() throws HttpMediaTypeNotAcceptableException {
when(request.getHeaderValues(HttpHeaders.ACCEPT)).thenReturn(new String[]{"application/something"});
assertThat(subject.resolveMediaTypes(request), containsInAnyOrder(
MediaType.valueOf("application/something"),
MediaType.APPLICATION_JSON
));
}
@Test
public void testAddsApplicationJsonToNull() throws HttpMediaTypeNotAcceptableException {
when(request.getHeaderValues(HttpHeaders.ACCEPT)).thenReturn(null);
assertThat(subject.resolveMediaTypes(request), contains(
MediaType.ALL // that's what the default does, which includes application/json, so... fine
));
}
@Test
public void testRetainsApplicationJson() throws HttpMediaTypeNotAcceptableException {
when(request.getHeaderValues(HttpHeaders.ACCEPT)).thenReturn(new String[]{"application/json"});
assertThat(subject.resolveMediaTypes(request), contains(MediaType.APPLICATION_JSON));
}
}