Spring @ExceptionHandler不能与@ResponseBody一起使用

时间:2011-02-23 21:08:42

标签: rest spring-mvc

我尝试为休息控制器配置一个spring异常处理程序,该处理程序能够根据传入的accept标头将映射呈现给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");
}

任何?

7 个答案:

答案 0 :(得分:20)

你的方法

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

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

  • 字符串
  • 的ModelAndView。

有关详细信息,请参阅http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html。以下是链接中的具体文字:

  

返回类型可以是String,其中   被解释为视图名称或   ModelAndView对象。

回应评论

  

Thanx,我似乎已经过度了。那是   坏...任何想法如何提供   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()));
}

答案 1 :(得分:13)

  

Thanx,我似乎已经过度了。这很糟糕......任何想法如何提供   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 ) );
}

答案 2 :(得分:10)

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

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

已在3.1 M1中修复了......

答案 3 :(得分:6)

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

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

答案 4 :(得分:6)

我正在使用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();
}
}

答案 5 :(得分: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" />

答案 6 :(得分: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<?>