项目难免会出现系统抛出异常或者404 。 直接把错误信息反馈给用户不太好。所以要统一处理异常并返回直观的提示。
【以下展示了常见的实现方法,应该算比较完整的】
一、统一返回指定错误页面 【4种方法】
-
方法1:使用继承
ErrorController
, (注:【能处理 404、500等错误码】这个也会处理到js
、css
等静态资源)- 后台抛出的异常,或者访问空的url都能处理。
package com.demo.controller;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class ExceptionController implements ErrorController {
@RequestMapping("error")
public String handleError(HttpServletRequest request){
HttpStatus status = this.getStatus(request);
if(status.value() == 404){
return "404"; // 返回404页面
}else if(status.value() == 500){
return "500"; // 返回 500 页面
}
return "error"; // 返回其它错误的页面
}
@Override
public String getErrorPath() {
return "error"; // 如果异常则请求到此url (这里是请求到/error),可以自定义
}
/**
* 获取状态码
* @param request
* @return
*/
protected HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
} else {
try {
return HttpStatus.valueOf(statusCode.intValue());
} catch (Exception var4) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}
}
}
- 其它的一些信息可以通过
request.getAttribute("属性");
获取,常见属性:
属性 | 描述 |
---|---|
javax.servlet.error.status_code | 该属性给出状态码,状态码可被存储,并在存储为 java.lang.Integer 数据类型后可被分析。 |
javax.servlet.error.exception_type | 该属性给出异常类型的信息,异常类型可被存储,并在存储为 java.lang.Class 数据类型后可被分析。 |
javax.servlet.error.message | 该属性给出确切错误消息的信息,信息可被存储,并在存储为 java.lang.String 数据类型后可被分析。 |
javax.servlet.error.request_uri | 该属性给出有关 URL 调用 Servlet 的信息,信息可被存储,并在存储为 java.lang.String 数据类型后可被分析。 |
javax.servlet.error.exception | 该属性给出异常产生的信息,信息可被存储,并在存储为 java.lang.Throwable 数据类型后可被分析。 |
javax.servlet.error.servlet_name | 该属性给出 Servlet 的名称,名称可被存储,并在存储为 java.lang.String 数据类型后可被分析。 |
-
完整属性看下图:
比如获取异常信息:
/**
* 获取异常信息
* @param request
* @return
*/
protected HttpStatus getException(HttpServletRequest request) {
String e= (Integer)request.getAttribute("javax.servlet.error.exception"); // 主要是这句
}
-
方法2:使用
@ControllerAdvice
和@ExceptionHandler
注解 【只能处理 500等服务错误码,无法处理404 , 并且无法处理filter抛出的异常】
package com.demo.config;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class) // 这里是要处理的异常,可以换成其它的
public String defaultErrorHandler(HttpServletRequest request , HttpServletResponse response, Throwable e){
// 这里可以做一些其它的操作。 可以获取完整的异常信息
return "500" ; // 返回的字符串会匹配上对应页面 。500 对应 500.html
}
}
- 附上官方教程 Error Handling (打开网页后请等待加载完成,会自动跳转到 Error Handling
)
-
方法3:最简单的方法 ,在
/resources/public/error
下错误页面 只适用于返回错误页面,可以处理404、500等错误-
/resources/public/error/404.html
自动展示404错误,/resources/public/error/500.html
自动展示500错误页面。可以自已添加其它错误码的页面
-
- 附上官方教程 Custom Error Pages ,(打开网页后请等待加载完成,会自动跳转到 Custom Error Pages
)
-
方法4:通过配置
EmbeddedServletContainerCustomizer
来实现 【能处理 404、500等错误码】这个也会处理到js
、css
等静态资源
package com.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(ResumeApplication.class, args);
}
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return container -> {
// container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/error")); 这里还可以映射到controller上去处理
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404.html")); // 也可以映射到指定html文件上
container.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html"));
// 这里可以继续添加其它错误页面
};
}
}
二、统一返回 json ,针对 REST 风格的接口 【2种方法】
-
方法1:使用继承
ErrorController
, (注:这个也会处理到js
、css
等静态资源)
和上面的方式差不多类似 ,使用@RestController
而不是@Controller
注解
package com.demo.controller;
import com.veslay.resume.domain.ArticleContent;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController // 注意这里是RestController 而不是 Controller
public class ExceptionController implements ErrorController {
@RequestMapping("error")
// @ResponseBody 也可以使用这个注解
public ArticleContent handleError(HttpServletRequest request){
ArticleContent articleContent = new ArticleContent() ; // 这里可以是自定义的对象
articleContent.setNickName("nickName") ;
return articleContent; // 返回其它错误的页面
}
@Override
public String getErrorPath() {
return "error";
}
}
-
方法2:通过配置
EmbeddedServletContainerCustomizer
来实现 【能处理 404、500等错误码】这个也会处理到js
、css
等静态资源
package com.veslay.resume;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
@SpringBootApplication
public class ResumeApplication {
public static void main(String[] args) {
SpringApplication.run(ResumeApplication.class, args);
}
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return container -> {
// 这里的 /404 和 /500 是映射到controller的接口,接口返回json数据则ok .
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404"));
container.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500"));
// 这里可以继续添加其它错误页面
};
}
}
-
方法3:使用
@RestControllerAdvice
和@ExceptionHandler
注解 【只能处理 500等服务错误码,无法处理404】
package com.demo.config;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class) // 这里是要处理的异常,可以换成其它的
public ResponseModel defaultErrorHandler(HttpServletRequest request , HttpServletResponse response, Throwable e){
// 这里可以做一些其它的操作。 可以获取完整的异常信息
// ResponseModel 对象是自定义的,这个对象会自动转成json格式的
return ResponseModel.ERROR(e.getMessage()) ; /
}
}
- ResponseModel 对象是自定义的,这个对象会自动转成json格式的
{
"timestamp": "2018-07-13 15:11:42",
"status": 400,
"message": "数据无法获取",
"data": null
}
- 使用
@RestControllerAdvice
时如果后台报错org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
,前端状态码为406
,如下处理:
@ExceptionHandler(Exception.class) // 这里是要处理的异常,可以换成其它的
public ResponseModel defaultErrorHandler(HttpServletRequest request , HttpServletResponse response, Throwable e){
// 增加这个,移除请求头中的 text/plain
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
return ResponseModel.ERROR(e.getMessage()) ; /
}
这个错误场景是:前端是
react
,post
请求上传文件,并且在url路径中还传了参数?b=xxx&a=a
,接收后端返回json
, 后端解析上传的文件时异常,统一异常处理的类打算把500
的错误码改成200
并返回ResponseModel
对象,然后就出现了这个错误
- 最最重要的一点来了,点晴之笔,网上的类似教程就是没有说明这一点,所以导致很多都没有成功实现功能。 以上的每个方法都要加下面的核心配置 , 配置如下
- application.yml
spring:
mvc:
view:
suffix: .html
prefix: /
- application.properties
spring.mvc.view.prefix= /
spring.mvc.view.suffix= .html