Java 基于Accept头的Spring MVC-@ExceptionHandler
我有一个Java 基于Accept头的Spring MVC-@ExceptionHandler,java,spring,spring-mvc,Java,Spring,Spring Mvc,我有一个HandlerInterceptorAdapter,它拦截所有请求并执行用户授权检查。基本上: @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { User user = ... // get user checkIfAuthorized(user); // throws
HandlerInterceptorAdapter
,它拦截所有请求并执行用户授权检查。基本上:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
User user = ... // get user
checkIfAuthorized(user); // throws AuthorizationException
return true;
}
然后我有一个@ExceptionHandler
用于该授权异常
@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;
}
@ExceptionHandler(值=AuthorizationException.class)
公共响应处理未授权(授权例外e){
//基于内容类型的json/jsp/xml/其他类型的TODO自定义异常处理程序
ResponseEntity ResponseEntity=新ResponseEntity(“您无权访问该页面。”,HttpStatus.UNAUTHORIZED);
返回响应性;
}
如果(未经授权的)请求接受text/plain
(并且可以很容易地更改为json),则这是可以接受的。
如何为特定的接受头创建不同的@ExceptionHandler
s
@RequestMapping
具有products()
。@ExceptionHandler
是否有类似的功能?我认为有两种方法:
手动操作
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;
别忘了设置响应的状态代码。我知道这很晚才出现,但我一直在寻找解决方案,遇到了这个问题,找到了我认为更好的解决方案。您可以在@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对所有人都是一样的,但是它转发给一个控制器,这个控制器可以做一些普通的事情,不是完全相同的用例,而是相同的需求。我用一个定制的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();
}
}
@EnableWebMvc
@配置
@组件扫描({“com.foo”})
公共类应用程序配置实现WebMVCConfiguer{
...
@凌驾
公共无效配置MessageConverters(列表在第二个示例中,它将使用html。我不希望jsp以路径命名。在第一个示例中,我不想为text/html
返回视图名称,是否可以使用ResponseEntity
?看起来ResponseEntity更倾向于REST响应。您应该返回ModelAndView(或仅返回视图)对于这种情况。您建议将返回类型设置为简单的Object
,并根据标题返回其中的一个?是的。在这种情况下,Spring不关心类型安全。它会立即检查对象是什么。这很有意义,在我的情况下也起作用。在返回“forward:/error”之前,我必须调用request.setAttribute(“javax.servlet.error.status_code”,HttpStatus.NOT_FOUND.value());
(根据需要替换状态),因为AbstractErrorController使用该属性确定状态,然后将其映射到错误页。如果我没有设置该属性,它将默认为“200”“很明显,我没有为其提供错误页面。这是正确的。我后来还发现,在ErrorController中需要一个额外的@ExceptionHandler方法-确保它不会在错误控制器调用期间发生异常时通过将请求转发给self来循环(例如ClientBort,无论您的控制器有多安全都可能发生)谢谢,我实际上捕获到了Spring在从我的ErrorController返回响应后引发的HttpMediaTypeNotAcceptableException。这是在iOS Firefox(FxiOS)的不规则favicon请求上发生的。
@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());
}
...
}