Spring @ExceptionHandler 不适用于 @ResponseBody

Posted

技术标签:

【中文标题】Spring @ExceptionHandler 不适用于 @ResponseBody【英文标题】:Spring @ExceptionHandler does not work with @ResponseBody 【发布时间】:2011-07-03 02:23:11 【问题描述】:

我尝试为休息控制器配置一个弹簧异常处理程序,该处理程序能够根据传入的接受标头将映射呈现到 xml 和 json。它现在抛出一个 500 servlet 异常。

这行得通,它拿起了 home.jsp:

@ExceptionHandler(IllegalArgumentException.class)
public String handleException(final Exception e, final HttpServletRequest request, Writer writer)

    return "home";

这不起作用:

@ExceptionHandler(IllegalArgumentException.class)
public @ResponseBody Map<String, Object> handleException(final Exception e, final HttpServletRequest request, Writer writer)

    final Map<String, Object> map = new HashMap<String, Object>();
    map.put("errorCode", 1234);
    map.put("errorMessage", "Some error message");
    return map;

在同一控制器中,通过相应的转换器将响应映射到 xml 或 json:

@RequestMapping(method = RequestMethod.GET, value = "/book/id", headers = "Accept=application/json,application/xml")
public @ResponseBody
Book getBook(@PathVariable final String id)

    logger.warn("id=" + id);
    return new Book("12345", new Date(), "Sven Haiges");

【问题讨论】:

ExceptionHandler returning JSON or XML not working in spring mvc 3的可能重复 【参考方案1】:

我遇到了类似的问题,当您的 Controller 方法返回类型和 ExceptionHandler 返回类型不相同时会出现此问题。确保您具有完全相同的返回类型。

控制器方法:

@RequestMapping(value = "/id", produces = "application/json", method = RequestMethod.POST)
public ResponseEntity<?> getUserById(@PathVariable String id) throws NotFoundException 
    String response = userService.getUser(id);
    return new ResponseEntity(response, HttpStatus.OK);

建议方法:

@ExceptionHandler(NotFoundException.class)
public ResponseEntity<?> notFoundException(HttpServletRequest request, NotFoundException e) 
    ExceptionResponse response = new ExceptionResponse();
    response.setSuccess(false);
    response.setMessage(e.getMessage());
    return new ResponseEntity(response, HttpStatus.NOT_FOUND);

如您所见,两个类中的返回类型相同 ResponseEntity&lt;?&gt;

【讨论】:

【参考方案2】:

我使用的是 Spring 3.2.4。我对这个问题的解决方案是确保我从异常处理程序返回的对象具有 getter。

没有 getter,Jackson 无法将对象序列化为 JSON。

在我的代码中,对于以下 ExceptionHandler:

@ExceptionHandler(RuntimeException.class)
@ResponseBody
public List<ErrorInfo> exceptionHandler(Exception exception)
    return ((ConversionException) exception).getErrorInfos();

我需要确保我的 ErrorInfo 对象有 getter:

package com.pelletier.valuelist.exception;

public class ErrorInfo 
private int code;
private String field;
private RuntimeException exception;

public ErrorInfo()

public ErrorInfo(int code, String field, RuntimeException exception)
    this.code = code;
    this.field = field;
    this.exception = exception;


public int getCode() 
    return code;


public String getField() 
    return field;


public String getException() 
    return exception.getMessage();


【讨论】:

【参考方案3】:

AnnotationMethodHandlerExceptionResolver 也需要 MappingJacksonHttpMessageConverter

<bean
    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
    <property name="messageConverters">
        <list>
            <bean
                class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                <property name="objectMapper" ref="jacksonObjectMapper" />
            </bean>
        </list>
    </property>
</bean>

<bean id="jacksonObjectMapper"
    class="iacm.cemetery.framework.web.servlet.rest.JacksonObjectMapper" />

【讨论】:

我没有意识到 org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter 的 messageConverters 需要复制到 org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver。我想你知道的越多。【参考方案4】:

谢谢,看来我读多了。这很糟糕......任何想法如何提供 自动以 xml/json 格式出现异常?

Spring 3.0 中的新功能 MappingJacksonJsonView 可以用来实现:

private MappingJacksonJsonView  jsonView = new MappingJacksonJsonView();

@ExceptionHandler(Exception.class)
public ModelAndView handleAnyException( Exception ex )

    return new ModelAndView( jsonView, "error", new ErrorMessage( ex ) );

【讨论】:

自 Spring 4.0.0 MappingJacksonJsonView 已弃用其构造函数。见:docs.spring.io/spring/docs/4.0.5.RELEASE/javadoc-api/org/… @borjab 不是因为这是一种无效的方法,而是因为他们正在推动迁移到MappingJackson2JsonView【参考方案5】:

如果您使用消息转换器将错误对象编组为响应内容,则以下可能是一种解决方法

@ExceptionHandler(IllegalArgumentException.class)
public String handleException(final Exception e, final HttpServletRequest request)

    final Map<String, Object> map = new HashMap<String, Object>();
    map.put("errorCode", 1234);
    map.put("errorMessage", "Some error message");
    request.setAttribute("error", map);
    return "forward:/book/errors"; //forward to url for generic errors


//set the response status and return the error object to be marshalled
@SuppressWarnings("unchecked")
@RequestMapping(value = "/book/errors", method = RequestMethod.POST, RequestMethod.GET)
public @ResponseBody Map<String, Object> showError(HttpServletRequest request, HttpServletResponse response)

    Map<String, Object> map = new HashMap<String, Object>();
    if(request.getAttribute("error") != null)
        map = (Map<String, Object>) request.getAttribute("error");

    response.setStatus(Integer.parseInt(map.get("errorCode").toString()));

    return map;

【讨论】:

这绝对应该被选为这个话题的答案!谢谢你,伙计!【参考方案6】:

这似乎是一个已确认的错误 (SPR-6902 @ResponseBody 不适用于 @ExceptionHandler)

https://jira.springsource.org/browse/SPR-6902

虽然已在 3.1 M1 中修复...

【讨论】:

【参考方案7】:

你的方法

@ExceptionHandler(IllegalArgumentException.class)
public @ResponseBody Map<String, Object> handleException(final Exception e, final HttpServletRequest request, Writer writer)

不起作用,因为它具有错误的返回类型。 @ExceptionHandler 方法只有两种有效的返回类型:

字符串 模型和视图。

请参阅http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html 了解更多信息。以下是链接中的具体文字:

返回类型可以是字符串, 被解释为视图名称或 ModelAndView 对象。

回应评论

谢谢,看来我读多了。那是 坏...任何想法如何提供 在 xml/json 中自动出现异常 格式? – Sven Haiges 7 小时前

这是我所做的(我实际上是在 Scala 中完成的,所以我不确定语法是否完全正确,但你应该明白要点)。

@ExceptionHandler(Throwable.class)
@ResponseBody
public void handleException(final Exception e, final HttpServletRequest request,
        Writer writer)

    writer.write(String.format(
            "\"error\":\"java.class\":\"%s\", \"message\":\"%s\"",
            e.getClass(), e.getMessage()));

【讨论】:

谢谢,看来我读多了。这很糟糕......有什么想法如何以 xml/json 格式自动提供异常? 您好,只是为了评论@ExceptionHandler 方法的返回类型非常广泛。根据文档,它可以是 ModelAndView、Model 对象、java.util.Map、org.springframework.web.servlet.View、String 或 void 请注意,由于您没有返回类型 (void),因此 @ResponseBody 没有意义,可以省略。 是的,从 Spring 3.1 开始,@ResponseBody 方法可以返回一个对象,这将被传递给一个 HttpMessageConverter 以转换为一个表示。

以上是关于Spring @ExceptionHandler 不适用于 @ResponseBody的主要内容,如果未能解决你的问题,请参考以下文章

如何在@ExceptionHandler(Spring REST)中获取@RequestBody

@ExceptionHandler 用于 Spring 中的包装异常 / getCause()

spring 新版本 ExceptionHandler 了解

Spring的 @ExceptionHandler注解无效问题

Spring @ControllerAdvice/@ExceptionHandler 不起作用

Spring Async 最佳实践:ExceptionHandler