回顾
我在之前公布了一篇 spring 对立返回的文章,最初提到是无奈捕捉 404 异样的,这里咱们先来测试一下
@RestController
public class TestController {@GetMapping("/test")
public String insert22() {return "hello";}
}
浏览器申请试一下 http://localhost:8080/xxx 报错
# Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed Dec 29 10:14:36 CST 2021
There was an unexpected error (type=Not Found, status=404).
springboot 的解决形式
springboot 解决这个 404 的异样是在 BasicErrorController
中解决的
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
...........
@Override
public String getErrorPath() {return null;}
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
// 蕴含申请头 "Accept": "application/json" 会往这里走
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
.............
}
只有申请门路 /error 就能够进去到 errorHtml
这个办法,在浏览器申请 http://localhost:8080/error 就能够进入这个办法
解决方案
我这应用的 springboot 的版本为 2.3.7.RELEASE
计划 1:重写 /error 的申请
这种计划会间接舍弃掉 HTML 响应形式,然而前后端拆散模式下,后端曾经很少应用 ModelAndView 了
@Controller
public class NoFoundController extends AbstractErrorController {public NoFoundController(ErrorAttributes errorAttributes) {super(errorAttributes);
}
/**
* 默认门路 /error,能够通过 server.error.path 配置
*/
@RequestMapping(("${server.error.path:/error}"))
public ResponseEntity<Map<String, Object>> notFoundError(HttpServletRequest request, HttpServletResponse response) {Map<String, Object> map = new HashMap<>(3);
HttpStatus status = getStatus(request);
map.put("code", status.value());
map.put("data", null);
map.put("message", status.toString());
return new ResponseEntity<>(map, status);
}
/**
* 在 springboot2.3.0 新增了 server.error.path 进行配置, 这个废除应用了,之前版本能够间接通过设置这个返回值批改默认 /error 的门路
*/
@Override
public String getErrorPath() {return null;}
}
计划 2:重写 BasicErrorController 中的错误处理
这种形式无奈将 HTML 响应的也改成了 json 返回,申请中要有 "Accept": "application/json"
能力走 json 响应
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class MyBasicErrorController extends BasicErrorController {public MyBasicErrorController(ServerProperties serverProperties) {
// import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
super(new DefaultErrorAttributes(), serverProperties.getError());
}
/**
* JSON 响应
*/
@Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {Map<String, Object> map = new HashMap<>();
HttpStatus status = getStatus(request);
map.put("code", status.value());
map.put("data", null);
map.put("message", status.toString());
return new ResponseEntity<>(map, status);
}
/**
* HTML 响应, 依据需要解决本人解决
*/
@Override
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {return super.errorHtml(request, response);
}
}
其中 MyBasicErrorController
的构造函数能够参考 spring 主动拆卸 ErrorMvcAutoConfiguration
中的传值
// 源码:public class ErrorMvcAutoConfiguration {
private final ServerProperties serverProperties;
public ErrorMvcAutoConfiguration(ServerProperties serverProperties) {this.serverProperties = serverProperties;}
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
// ErrorAttributes
return new DefaultErrorAttributes();}
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes,
ObjectProvider<ErrorViewResolver> errorViewResolvers) {
// serverProperties.getError
return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
errorViewResolvers.orderedStream().collect(Collectors.toList()));
}
........
}
最初附上残缺代码:
@Getter
public class BusinessException extends RuntimeException {
private Integer code;
public BusinessException(Integer code, String message) {super(message);
this.code = code;
}
public BusinessException(String message) {super(message);
}
}
--------------------------------------------------------------------------------------------
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalException {@ExceptionHandler(value = BusinessException.class)
public ResponseModel<Void> businessExceptionError(BusinessException e) {log.error("业务异样", e);
if (e.getCode() != null) {return ResponseModel.error(e.getCode(), e.getMessage());
}
return ResponseModel.error(e.getMessage());
}
@ExceptionHandler(value = Exception.class)
public ResponseModel<Void> exceptionError(Exception e) {log.error("零碎异样", e);
return ResponseModel.error();}
}
--------------------------------------------------------------------------------------------
@Getter
public enum ResponseEnum {SUCCESS(0, "OK"),
PARAMETER_ERROR(1,"参数异样"),
NO_FOUND(404,"not found"),
SYSTEM_ERROR(500, "服务器异样,请分割管理员");
ResponseEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
private final Integer code;
private final String message;
}
--------------------------------------------------------------------------------------------
public class ResponseModel<T> {
private Integer code;
private String message;
private T data;
public ResponseModel(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static ResponseModel<Void> ok() {return ok(null);
}
public static <T> ResponseModel<T> ok(T data) {return new ResponseModel<>(ResponseEnum.SYSTEM_ERROR.getCode(), ResponseEnum.SYSTEM_ERROR.getMessage(), data);
}
public static <T> ResponseModel<T> ok(T data, String message) {return new ResponseModel<>(ResponseEnum.SYSTEM_ERROR.getCode(), message, data);
}
public static ResponseModel<Void> error(Integer statusCode, String message) {return new ResponseModel<>(statusCode, message, null);
}
public static ResponseModel<Void> error(String message) {return error(ResponseEnum.SYSTEM_ERROR.getCode(), message);
}
public static ResponseModel<Void> error() {return error(ResponseEnum.SYSTEM_ERROR.getCode(), ResponseEnum.SYSTEM_ERROR.getMessage());
}
}
--------------------------------------------------------------------------------------------
@Controller
public class NoFoundController extends AbstractErrorController {public NoFoundController(ErrorAttributes errorAttributes) {super(errorAttributes);
}
/**
* 默认门路 /error,能够通过 server.error.path 配置
*/
@RequestMapping(("${server.error.path:/error}"))
public ResponseEntity<Map<String, Object>> notFoundError(HttpServletRequest request, HttpServletResponse response) {Map<String, Object> map = new HashMap<>(3);
HttpStatus status = getStatus(request);
map.put("code", status.value());
map.put("data", null);
map.put("message", status.toString());
return new ResponseEntity<>(map, status);
}
/**
* 在 springboot2.3.0 新增了 server.error.path 进行配置, 这个废除应用了,之前版本能够间接通过设置这个返回值批改默认 /error 的门路
*/
@Override
public String getErrorPath() {return null;}
}
感激各位小伙伴浏览到最初,如有谬误,敬请斧正。