5

我有一个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为特定的s制作不同的sAccept标题制作不同的 s?

@RequestMappingproduces()。有类似的东西@ExceptionHandler吗?

4

3 回答 3

3

我知道这来晚了,但我一直在寻找解决方案,遇到了这个问题并找到了我认为更好的解决方案。您可以在 @ExceptionHandler 中返回“forward:/error”(返回字符串)以将请求转发到

@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 对所有人都是一样的,但是转发给一个可以做通常事情的控制器

于 2017-05-16T10:14:35.640 回答
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;

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

于 2013-06-26T14:20:55.807 回答
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());
    }

    ...
}
于 2018-05-31T14:37:25.963 回答