上一篇:《??SpringBoot对Spring MVC都做了哪些事?(二)??》错误处理默认情况下,SpringBoot提供了一个/error映射,以合理的方式处理所有错误,并在servlet容器中注册为“全局”错误页面。对于机器客户端,它会生成一个JSON响应,其中包含错误、HTTP状态和异常消息的详细信息。对于浏览器客户端,有一个“whitelabel”错误视图以HTML格式呈现相同的数据(要自定义它,添加一个解决错误的视图)。如果你想定制默认的错误处理行为,你可以设置一些server.error属性。要完全替换默认行为,要么实现ErrorController并注册该类型的bean定义,要么添加一个ErrorAttributes类型的bean以使用现有机制,但替换内容。您还可以定义一个用@ControllerAdvice注释的类,以针对特定控制器和/或异常类型以自定义JSON格式返回,如以下示例所示:@ControllerAdvice(basePackageClasses=AcmeController.class)(YourException.class)@ResponseBodyResponseEntity>handleControllerException(HttpServletRequestrequest,Throwableex){HttpStatusstatus=getStatus(request);返回新的ResponseEntity<>(newCustomErrorType(status.value(),ex.getMessage()),status);}privateHttpStatusgetStatus(HttpServletRequestrequest){IntegerstatusCode=(Integer)request.getAttribute("javax.servlet.error.status_code");如果(statusCode==null){返回HttpStatus.INTERNAL_SERVER_ERROR;}返回HttpStatus.valueOf(statusCode);在前面的示例中,如果您的异常是由与AcmeController在同一包中定义的控制器抛出的,那么将使用CustomErrorTypePOJO的JSON表示而不是ErrorAttributes表示。自定义错误页面如果要为给定状态代码显示自定义HTML错误页面,可以将文件添加到/error目录。错误页面可以是静态HTML(即添加到任何静态资源目录),也可以使用模板构建。文件名应该是准确的状态代码或序列掩码。例如将404映射为静态HTML文件,目录结构如下:src/+-main/+-java/|+<源代码>+-资源/+-公共/+-错误/|+-404.html+-使用FreeMarker模板映射所有5xx错误,目录结构如下:src/+-main/+-java/|+<源代码>+-资源/+-模板/+-错误/|+-5xx.ftlh+-对于更复杂的映射,您还可以添加实现ErrorViewResolver接口的bean,如下例所示:status,Mapmodel){//使用request或者status可选的返回一个ModelAndViewreturn...}}系统默认提供的DefaultErrorViewResolver在这个类中,我们可以看到错误页面是从默认情况下位于以下位置。公共类DefaultErrorViewResolver实现ErrorViewResolver,Ordered{privatestaticfinalMapSERIES_VIEWS;static{Mapviews=newEnumMap<>(Series.class);views.put(Series.CLIENT_ERROR,"4xx");views.put(Series.SERVER_ERROR,"5xx");SERIES_VIEWS=Collections.unmodifiableMap(views);}@OverridepublicModelAndViewresolveErrorView(HttpServletRequestrequest,HttpStatusstatus,Mapmodel){ModelAndViewmodelAndView=resolve(String.valueOf(status.value()),模型);if(modelAndView==null&&SERIES_VIEWS.containsKey(status.series())){//默认进入这里,根据错误代码的顺序进行解析视图modelAndView=resolve(SERIES_VIEWS.get(status.series()),model);}returnmodelAndView;}privateModelAndViewresolve(StringviewName,Mapmodel){StringerrorViewName="error/"+viewName;//...返回resolveResource(errorViewNa我,模特);}privateModelAndViewresolveResource(StringviewName,Mapmodel){//从下面路径搜索(路径会和error/目录拼接)//classpath:/META-INF/resources///classpath:/resources///classpath:/static///classpath:/public///以上路径可以通过spring.web.resources.staticLocations进行配置for(Stringlocation:this.resources.getStaticLocations()){try{资源resource=this.applicationContext.getResource(location);resource=resource.createRelative(viewName+".html");如果(resource.exists()){返回新的ModelAndView(newHtmlResourceView(resource),model);}}}返回空值;}}SpringMVC之外的映射错误页面对于不使用SpringMVC的应用程序,可以使用ErrorPageRegistrar接口直接注册ErrorPages。这种抽象直接与底层嵌入式servlet容器一起工作,即使您没有SpringMVCDispatcherServlet,也可以工作。自定义错误错误页面@BeanpublicErrorPageRegistrarerrorPageRegistrar(){returnnewMyErrorPageRegistrar();}privatestaticclassMyErrorPageRegistrarimplementsErrorPageRegistrar{@OverridepublicvoidregisterErrorPages(ErrorPageRegistryregistry){registry.addErrorPages(newErrorPage(HttpStatus.BAD_REQUEST,"/400"));}}工作原理TomcatServletWebServerFactorypublicclassTomcatServletWebServerFactoryextendsAbstractServletWebServerFactoryimplementsConfigurableTomcatWebServerFactory,ResourceLoaderAware{privateSeterrorPages=newLinkedHashSet<>();publicWebServergetWebServer(ServletContextInitializer...initializers){prepareContext(tomcat.getHost(),initializers);}protectedvoidprepareContext(Hosthost,ServletContextInitializer[]initializers){configureContext(context,initializersToUse);}protectedvoidconfigureContext(Contextcontext,ServletContextInitializer[]initializers){TomcatStarterstarter=newTomcatStarter(initializers);//遍历所有的ErrorPage对象并注册到Tomcat容器中.ErrorPage();tomcatErrorPage.setLocation(errorPage.getPath());tomcatErrorPage.setErrorCode(errorPage.getStatusCode());tomcatErrorPage.setExceptionType(errorPage.getExceptionName());上下文.addErrorPage(tomcatErrorPage);}}}注册处理器//在这个自动配置类中,会注册一个BeanPostProcessorsRegistrar处理器@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,...})publicclassServletWebServerFactoryAutoConfiguration{}注册错误页面注册器注册了BeanPostProcessorsRegistrar处理器在前一步。在此处理器中,将注册一个ErrorPageRegistrarBeanPostProcessor错误页面注册器处理器。publicstaticclassBeanPostProcessorsRegistrarimplementsImportBeanDefinitionRegistrar,BeanFactoryAware{publicvoidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry){//...//注册ErrorPageRegistrarBeanPostProcessor,又是一个处理器registerSyntheticBeanIfMissing(registry,"errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class,ErrorPageRegistrarBeanPostProcessor::新的);}}注册错误页面publicclassErrorPageRegistrarBeanPostProcessorimplementsBeanPostProcessor,BeanFactoryAware{私有列表注册商;@OverridepublicvoidsetBeanFactory(BeanFactorybeanFactory){this.beanFactory=(ListableBeanFactory)beanFactory;}@OverridepublicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)throwsBeansException{//判断当前Bean是否为ErrorPageRegistry对象。在9.2中,我们在最后提到了TomcatServletWebServerFactory类实现//ErrorPageRegistry接口if(beaninstanceofErrorPageRegistry){//处理postProcessBeforeInitialization((ErrorPageRegistry)bean);}返回豆;}@OverridepublicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException{returnbean;}//registry=TomcatServletWebServerFactoryprivatevoidpostProcessBeforeInitialization(ErrorPageRegistryregistry){//获取容器中所有ErrorPageRegistrar类型的Bean对象for(ErrorPageRegistrarregistrar:getRegistrars()){//在TomcatServletWebServerFactory中注册错误页面registrar.registerErrorPages(registry);}}privateCollectionget(){if(this.registrars==null){//从容器中获取所有ErrorPageRegistrar类型的bean//容器默认注册一个ErrorPageRegistrarthis.registrars=newArrayList<>(this.beanFactory.getBeansOfType(ErrorPageRegistrar.class,false,false).values());this.registrars.sort(AnnotationAwareOrderCompar实例);this.registrars=Collections.unmodifiableList(this.registrars);}返回this.registrars;}}以上流程实现了自定义错误页面的注册,实现了跨域支持跨域资源共享(CORS)。大多数浏览器实现的W3C规范允许您以灵活的方式指定授权哪些跨源请求。而不是使用一些不太安全且功能较弱的方法,如IFRAME或JSONP。从4.2版本开始,SpringMVC支持CORS。在SpringBoot应用程序中使用带有@CrossOriginCORS配置注释的控制器方法不需要任何特定配置。可以通过使用自定义addCorsMappings(CorsRegistry)方法注册WebMVCConfigurerbean来定义全局CORS配置,如以下示例所示:@Configuration(proxyBeanMethods=false)(){@OverridepublicvoidaddCorsMappings(CorsRegistry注册表){注册表。addMapping("/api/**");}};}}