Spring MVC - 基于Accept标头的@ExceptionHandler

时间:2013-06-26 14:02:21

标签: java spring spring-mvc

我有HandlerInterceptorAdapter拦截所有请求并执行用户授权检查。非常基本:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    User user = ... // get user
    checkIfAuthorized(user); // throws AuthorizationException
    return true;
}

然后我为@ExceptionHandler提供了AuthorizationException

@ExceptionHandler(value = AuthorizationException.class) 
public ResponseEntity<String> handleNotAuthorized(AuthorizationException e) {
    // TODO Custom EXCEPTION HANDLER for json/jsp/xml/other types, based on content type
    ResponseEntity<String> responseEntity = new ResponseEntity<>("You are not authorized to access that page.", HttpStatus.UNAUTHORIZED);
    return responseEntity;
}

如果(未经授权的)请求接受text/plain(并且可以为json轻松更改),这很好。 如何为特定的@ExceptionHandler标题创建不同的Accept

@RequestMappingproduces()@ExceptionHandler是否有类似内容?

3 个答案:

答案 0 :(得分:2)

我想到两种方法:

手动

public ResponseEntity<String> handleNotAuthorized(AuthorizationException e, HttpServletRequest request) {
    // TODO Custom EXCEPTION HANDLER for json/jsp/xml/other types, based on content type
    if (/*read header accept from request and build appropiate response*/) {}
    ResponseEntity<String> responseEntity = new ResponseEntity<>("You are not authorized to access that page.", HttpStatus.UNAUTHORIZED);
    return responseEntity;

<强>自动

@ResponseBody
public SomeObject handleNotAuthorized(AuthorizationException e, HttpServletRequest request) {
    // TODO Custom EXCEPTION HANDLER for json/jsp/xml/other types, based on content type
    /* Construct someObject and let Spring MessageConverters transform it to JSON or XML. I don't remember what happens in case of HTML (it should go to a view)*/
    return someObject;

不要忘记设置响应的状态代码。

答案 1 :(得分:1)

我知道这件事迟到了,但我一直在寻找解决方案,遇到了这个问题并找到了我认为更好的解决方案。您可以返回&#34;转发:/错误&#34;在@ExceptionHandler中(返回一个String)将请求转发给

@RequestMapping("/error")
ErrorController {...}

并使用

@RequestMapping(produces = "text/html") 
ModelAndView errorPage() {...}

在ErrorController的一个方法上,

@RequestMapping(produces = "application/json") // or no 'produces' attribute for a default
MyJsonObject errorJson() {...} on another.

我认为这是一个非常巧妙的方法,它可能已经在那里,但我在尝试查找它时没有找到它。

所以基本上@ExceptionHandler对所有人都是一样的,但转发到可以做通常的东西的控制器

答案 2 :(得分:0)

不完全相同的用例,但要求相同。我用自定义的HttpMessageConverter实现来解决它。

@RestController
@RequestMapping("/foo")
public class MyResource {

    @GetMapping(path = "/{id}", produces = "application/json")
    public ResponseEntity<MyDto> get (@PathVariable(ID) long id)
            throws IOException {

        throw new MyCustomException();
    }

    @GetMapping(path = "/{id}/export", produces = "application/zip")
    public ResponseEntity<byte[]> export (@PathVariable(ID) long id)
            throws IOException {

        throw new MyCustomException();
    }
}

...

@ControllerAdvice
public class MyCustomExceptionHandler {

    @ResponseBody
    @ExceptionHandler
    @ResponseStatus(BAD_REQUEST)
    public JsonAPIErrorDocument handleException (MyCustomException e) {

        return ....;
    }
}

...

public class JsonAPIErrorDocumentToByteArrayMessageConverter extends AbstractHttpMessageConverter {

    public ErrorDocumentToByteArrayMessageConverter () {

        super(new MediaType("application", "zip"), MediaType.ALL);
    }

    @Override
    protected boolean supports (Class clazz) {

        return JsonAPIErrorDocument.class == clazz;
    }

    @Override
    protected Object readInternal (Class clazz, HttpInputMessage inputMessage)
            throws IOException,
            HttpMessageNotReadableException {

        return new byte[0];
    }

    @Override
    protected void writeInternal (Object t, HttpOutputMessage outputMessage)
            throws IOException,
            HttpMessageNotWritableException {

    }
}

...

@EnableWebMvc
@Configuration
@ComponentScan({ "com.foo" })
public class ApplicationConfig implements WebMvcConfigurer {

    ...

    @Override
    public void configureMessageConverters (List<HttpMessageConverter<?>> converters) {

        converters.add(new MappingJackson2HttpMessageConverter(objectMapper));
        converters.add(new ByteArrayHttpMessageConverter());
        converters.add(new JsonAPIErrorDocumentToByteArrayMessageConverter());
    }

    ...
}