在一个web项目中,总须要对一些错误进行界面或者json数据返回,已实现更好的用户体验,SpringBoot中提供了对于错误处理的自动配置html
ErrorMvcAutoConfiguration
这个类存放了全部关于错误信息的自动配置。java
访问步骤:web
SpringBoot
注册错误请求/error
。经过ErrorPageCustomizer
组件实现BasicErrorController
处理/error
,对错误信息进行了自适应处理,浏览器会响应一个界面,其余端会响应一个json
数据DefaultErrorViewResolver
类来进行具体的解析。能够经过模板引擎解析也能够解析静态资源文件,若是二者都不存在则直接返回默认的错误JSON
或者错误View
DefaultErrorAttributes
来添加具体的错误信息源代码spring
//错误信息的自动配置 public class ErrorMvcAutoConfiguration { //响应具体的错误信息 @Bean public DefaultErrorAttributes errorAttributes() { return } //处理错误请求 @Bean public BasicErrorController basicErrorController() { return } //注册错误界面 @Bean public ErrorMvcAutoConfiguration.ErrorPageCustomizer errorPageCustomizer() { return }
//注册错误界面,错误界面的路径为/error private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered { //服务器基本配置 private final ServerProperties properties; public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { //获取服务器配置中的错误路径/error ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath())); //注册错误界面 errorPageRegistry.addErrorPages(new ErrorPage[]{errorPage}); } //this.properties.getError() public class ServerProperties{ //错误信息的配置文件 private final ErrorProperties error = new ErrorProperties(); } //getPath public class ErrorProperties { @Value("${error.path:/error}") private String path = "/error";
//处理/error请求,从配置文件中取出请求的路径 @RequestMapping({"${server.error.path:${error.path:/error}}"}) public class BasicErrorController extends AbstractErrorController { //浏览器行为,经过请求头来判断,浏览器返回一个视图 @RequestMapping( produces = {"text/html"} public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = this.getStatus(request); Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); ModelAndView modelAndView = this.resolveErrorView(request, response, status, model); return modelAndView != null ? modelAndView : new ModelAndView("error", model); } //其余客户端行为处理,返回一个JSON数据 @RequestMapping public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { HttpStatus status = this.getStatus(request); if (status == HttpStatus.NO_CONTENT) { return new ResponseEntity(status); } else { Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL)); return new ResponseEntity(body, status); } }
//添加错误信息 public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered { public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap(); errorAttributes.put("timestamp", new Date()); this.addStatus(errorAttributes, webRequest); this.addErrorDetails(errorAttributes, webRequest, includeStackTrace); this.addPath(errorAttributes, webRequest); return errorAttributes; }
步骤:json
SpringBoot
建立错误请求/error
BasicErrorController
处理请求@RequestMapping( produces = {"text/html"} ) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = this.getStatus(request); Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); //解析错误界面,返回一个ModelAndView,调用父类AbstractErrorController的方法 ModelAndView modelAndView = this.resolveErrorView(request, response, status, model); return modelAndView != null ? modelAndView : new ModelAndView("error", model); }
public abstract class AbstractErrorController{ private final List<ErrorViewResolver> errorViewResolvers; protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) { Iterator var5 = this.errorViewResolvers.iterator(); //遍历全部的错误视图解析器 ModelAndView modelAndView; do { if (!var5.hasNext()) { return null; } ErrorViewResolver resolver = (ErrorViewResolver)var5.next(); //调用视图解析器的方法, modelAndView = resolver.resolveErrorView(request, status, model); } while(modelAndView == null); return modelAndView; } }
public interface ErrorViewResolver { ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model); }
//处理视图跳转 public DefaultErrorViewResolver{ public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) { //将状态码做为视图名称传入解析 ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model); if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) { modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model); } return modelAndView; } //视图名称为error文件夹下的400.html等状态码文件 private ModelAndView resolve(String viewName, Map<String, Object> model) { String errorViewName = "error/" + viewName; //是否存在模板引擎进行解析 TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext); //存在则返回解析之后的数据,不存在调用resolveResource方法进行解析 return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model); } //若是静态资源文件中存在,返回静态文件下的,若是不存在返回SpringBoot默认的 private ModelAndView resolveResource(String viewName, Map<String, Object> model) { String[] var3 = this.resourceProperties.getStaticLocations(); int var4 = var3.length; for(int var5 = 0; var5 < var4; ++var5) { String location = var3[var5]; try { Resource resource = this.applicationContext.getResource(location); resource = resource.createRelative(viewName + ".html"); if (resource.exists()) { return new ModelAndView(new DefaultErrorViewResolver.HtmlResourceView(resource), model); } } catch (Exception var8) { } } return null; }
应用:浏览器
在BasicErrorController
处理/error
请求的时候不适用浏览器默认请求springboot
@RequestMapping public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { HttpStatus status = this.getStatus(request); if (status == HttpStatus.NO_CONTENT) { return new ResponseEntity(status); } else { //调用父类的方法获取全部的错误属性 Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL)); return new ResponseEntity(body, status); } }
父类方法: protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) { WebRequest webRequest = new ServletWebRequest(request); //调用ErrorAttributes接口的getErrorAttributes方法, return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace); }
添加错误信息 public class DefaultErrorAttributes{ public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap(); errorAttributes.put("timestamp", new Date()); this.addStatus(errorAttributes, webRequest); this.addErrorDetails(errorAttributes, webRequest, includeStackTrace); this.addPath(errorAttributes, webRequest); return errorAttributes; }
返回的json数据有:服务器
能够经过模板引擎获取这些值app
SpringMVC提供的注解,能够用来定义全局异常,全局数据绑定,全局数据预处理ide
@ControllerAdivice
定义全局的异常处理
@ExceptionHandler(XXXException.class)
执行该方法须要处理什么异常,而后返回什么数据或者视图//json数据返回 ,处理自定义用户不存在异常 @ResponseBody @ExceptionHandler(UserException.class) public Map<String,String> userExceptionMethod(UserException us){ Map<String,String> map = new HashMap<>(); map.put("message",us.getMessage()); return map ; }
@ControllerAdvice
定义全局数据
@ModelAttribute(Name="key")
定义全局数据的keyController
中经过Model
获取对应的key的值@ControllerAdvice public MyConfig{ @ModelAttribute(name = "key") public Map<String,String> defineAttr(){ Map<String,String> map = new HashMap<>(); map.put("message","幻听"); map.put("update","许嵩"); return map ; } @Controller public UserController{ @GetMapping("/hello") public Map<String, Object> hello(Model model){ Map<String, Object> asMap = model.asMap(); System.out.println(asMap); //{key={message='上山',update='左手一式太极拳'}} return asMap ; } }
@ControllerAdvice
处理预处理数据(当须要添加的实体,属性名字相同的时候)
Controller
的参数中添加ModelAttribute
做为属性赋值的前缀ControllerAdvice
修饰的类中,结合InitBinder
来绑定对应的属性(该属性为ModelAttribite的value值@InitBinder
修饰的方法中经过WebDataBinder
添加默认的前缀@Getter@Setter public class Book { private String name ; private int age ; @Getter@Setter public class Music { private String name ; private String author ; //这种方式的处理,spring没法判断Name属性给哪一个bean赋值,因此须要经过别名的方式来进行赋值 @PostMapping("book") public String book(Book book , Music music){ System.out.println(book); System.out.println(music); return "404" ; } //使用如下的方式 @PostMapping("/book") public String book(@ModelAttribute("b")Book book , @ModelAttribute("m")Music music){ System.out.println(book); System.out.println(music); return "404" ; } public MyCOnfiguration{ @InitBinder("b") public void b(WebDataBinder webDataBinder){ webDataBinder.setFieldDefaultPrefix("b."); } @InitBinder("m") public void m(WebDataBinder webDataBinder){ webDataBinder.setFieldDefaultPrefix("m."); } }
浏览器和其余客户端都只能获取json
数据
@ControllerAdvice public class MyExceptionHandler { //处理UserException异常 @ResponseBody @ExceptionHandler(UserException.class) public Map<String,String> userExceptionMethod(UserException us){ Map<String,String> map = new HashMap<>(); map.put("message",us.getMessage()); map.put("status","500"); return map ; }
@ExceptionHandler(UserException.class) public String allException(UserException e,HttpServletRequest request){ Map<String,String> map = new HashMap<>(); map.put("message",e.getMessage()); map.put("load","下山"); request.setAttribute("myMessage",map); //设置状态码,SpringBoot经过java.servlet.error.status_code来设置状态码 request.setAttribute("javax.servlet.error.status_code",400); return "forward:/error" ; }
当抛出UserException
异常的时候,来到这个异常处理器,给这个请求中添加了数据,再转发到这个error请求中,交给ErrorPageCustomizer
处理,因为设置了请求状态码400
则返回的视图为400或4XX视图,或者直接返回一个JSON
数据
{ "timestamp": "2020-02-19T04:17:43.394+0000", "status": 400, "error": "Bad Request", "message": "用户名不存在异常", "path": "/crud/user/login" }
前面提到SpringBoot对错误信息的定义存在于DefaultErrorAttributes类的getErrorAttributes中,咱们能够直接继承这个类,或者实现ErrorAttributes接口,而后将咱们本身实现的错误处理器添加到容器中便可。
继承DefaultErrorAttributes
和实现ErrorAttributes
接口的区别是,继承之后仍然可使用SpringBoot默认的错误信息,咱们仅仅对该错误信息进行了加强;实现了ErrorAttributes
接口,彻底自定义错误信息
ErrorAttributes
接口public class MyErrorHandler implements ErrorAttributes { public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap(); errorAttributes.put("timestamp", new Date()); errorAttributes.put("status",500); errorAttributes.put("message","用户不存在异常"); return errorAttributes; } @Override public Throwable getError(WebRequest webRequest) { return null; }
DefaultErrorAttributes
的方法public class MyErrorHandler extends DefaultErrorAttributes { public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { //调用父类方法,直接在默认错误信息的基础上添加 Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest,includeStackTrace); errorAttributes.put("timestamp", new Date()); errorAttributes.put("message","用户不存在异常"); return errorAttributes; } }
@Component
组件直接将MyErrorHandler
组件添加到容器中@Bean
在配置类中将组件添加到容器中@Bean public DefaultErrorAttributes getErrorHandler(){ return new MyErrorHandler(); }
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest,includeStackTrace); errorAttributes.put("timestamp", new Date()); errorAttributes.put("message","用户不存在异常"); //指定从哪一个做用域中取值 webRequest.getAttribute("myMessage", RequestAttributes.SCOPE_REQUEST); return errorAttributes;
将在异常处理器中定义的错误信息取出,而后添加到错误信息中。