为了让客户端能有一个更好的体验,当客户端发送请求到服务端发生错误时服务端应该明确告诉客户端错误信息。
SpringBoot内置的异常处理返回的界面太杂乱,不够友好。我们需要将异常信息做封装处理响应给前端。本文介绍的为将错误信息统一封装成如下格式json响应给前端。
{
code:10001,
message:xxxxx,
request:GET url
}
自定义异常类
package com.lin.missyou.exception;
public class HttpException extends RuntimeException{
protected Integer code;
protected Integer httpStatusCode;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public Integer getHttpStatusCode() {
return httpStatusCode;
}
public void setHttpStatusCode(Integer httpStatusCode) {
this.httpStatusCode = httpStatusCode;
}
}
package com.lin.missyou.exception;
public class NotFoundException extends HttpException{
public NotFoundException(int code){
this.httpStatusCode = 404;
this.code = code;
}
}
package com.lin.missyou.exception;
public class ForbiddenException extends HttpException{
public ForbiddenException(int code){
this.httpStatusCode = 403;
this.code = code;
}
}
创建一个用于封装异常信息的类UnifyResponse
package com.lin.missyou.core;
public class UnifyResponse {
private int code;
private String message;
private String request;
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
public String getRequest() {
return request;
}
public UnifyResponse(int code, String message, String request) {
this.code = code;
this.message = message;
this.request = request;
}
}
将异常信息写在配置文件exception-code.properties
里
lin.codes[10000] = 通用异常
lin.codes[10001] = 通用参数错误
自定义配置类管理配置文件
package com.lin.missyou.core.configuration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@PropertySource(value="classpath:config/exception-code.properties")
@ConfigurationProperties(prefix = "lin")
@Component
public class ExceptionCodeConfiguration {
private Map<Integer,String> codes = new HashMap<>();
public Map<Integer, String> getCodes() {
return codes;
}
public void setCodes(Map<Integer, String> codes) {
this.codes = codes;
}
public String getMessage(int code) {
String message = codes.get(code);
return message;
}
}
创建一个全局异常处理类GlobalExceptionAdvice
,用@ControllerAdvice标明异常处理类。@ResponseStatus用于指定http状态码。
@ExceptionHandler标明异常处理器,传入参数指定当前函数要处理哪种类型的异常。Springboot会帮我们把这些异常信息传入到函数。第一个函数用于处理未知异常,不需要向前端提供详细的错误原因,只需提示统一的文本信息即可。第二个函数用于处理已知异常,需要指明具体的错误原因,需要根据Exception传递过来的信息灵活的定制httpStatusCode。ResponseEntity
可以自定义很多属性,包括可以设置httpheaders,httpbodys,httpStatus。
package com.lin.missyou.core;
import com.lin.missyou.core.config.ExceptionCodeConfiguration;
import com.lin.missyou.exception.HttpException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
@ControllerAdvice
public class GlobalExceptionAdvice {
@Autowired
ExceptionCodeConfiguration exceptionCodeConfiguration ;
@ExceptionHandler(Exception.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public UnifyResponse handleException(HttpServletRequest req,Exception e){
String method = req.getMethod();
String requestUrl = req.getRequestURI();
System.out.println(e);
UnifyResponse unifyResponse = new UnifyResponse(9999,"服务器错误",method+" "+requestUrl);
return unifyResponse;
}
@ExceptionHandler(HttpException.class)
public ResponseEntity<UnifyResponse> handleHttpException(HttpServletRequest req, HttpException e){
String method = req.getMethod();
String requestUrl = req.getRequestURI();
System.out.println(e);
UnifyResponse unifyResponse = new UnifyResponse(e.getCode(),exceptionCodeConfiguration.getMessage(e.getCode()),method+" "+requestUrl);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpStatus httpStatus = HttpStatus.resolve(e.getHttpStatusCode());
ResponseEntity<UnifyResponse> responseEntity = new ResponseEntity(unifyResponse,httpHeaders,httpStatus);
return responseEntity;
}
//参数校验
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public UnifyResponse handleBeanValidation(HttpServletRequest req, MethodArgumentNotValidException e){
String method = req.getMethod();
String requestUrl = req.getRequestURI();
List<ObjectError> errors = e.getBindingResult().getAllErrors();
String message = formatAllErrorMessages(errors);
return new UnifyResponse(10001,message,method+" "+requestUrl);
}
private String formatAllErrorMessages(List<ObjectError> errors){
StringBuffer errorMsg = new StringBuffer();
errors.forEach(error ->
errorMsg.append(error.getDefaultMessage()).append(";")
);
return errorMsg.toString();
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public UnifyResponse handleConstrainException(HttpServletRequest req, ConstraintViolationException e){
String method = req.getMethod();
String requestUrl = req.getRequestURI();
String message = e.getMessage();
return new UnifyResponse(10001,message,method+" "+requestUrl);
}
}
响应信息可能会出现乱码现象,修改配置文件编码。在设置面板搜索File Encodings
,Default encoding for properties files选择UTF-8,勾选Transparent native-to-ascii conversion