mirror of
https://gitee.com/wangkang/udf.git
synced 2026-10-21 04:43:41 +00:00
193 lines
7.3 KiB
Markdown
193 lines
7.3 KiB
Markdown
# spring boot / cloud (十三) 404错误处理进阶
|
|
|
|
## **前言**
|
|
|
|
在上一篇文章中介绍了[spring boot 官方文档](http://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/htmlsingle/#boot-features-error-handling)推荐的异常处理方式.承接上一篇文章,我们来了一下如何更好的处理404错误.
|
|
|
|
在[spring boot / cloud (二) 规范响应格式以及统一异常处理](https://my.oschina.net/wangkang80/blog/908919)这篇文章的最后跟大家提到了如下的配置
|
|
|
|
``` java
|
|
spring.mvc.throw-exception-if-no-handler-found=true
|
|
spring.resources.add-mappings=false
|
|
```
|
|
|
|
这两行配置的主要意思是告诉spring,如果你请求的地址,没有找到对应的handler,则抛出异常,默认配置是fase,同时也要关掉静态资源的映射,不然并不会起作用.
|
|
|
|
所以我们知道了,在spring中,404并不是是一个异常,而是一个错误,所走的处理方式也是不一样的.
|
|
|
|
## **场景分析**
|
|
|
|
按照以上的配置,我们可以轻易的将404错误也变成一个异常,然后走统一的异常处理方式,但是这样做是有代价的,就是也会关闭掉spring boot的经验资源mapping功能.
|
|
|
|
(大家可查阅[spring boot官方文档](https://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-static-content),来了解如何处理静态资源)
|
|
|
|
显然,这样做不太友好,因为不可避免的会有在后端服务中包加入一些静态界面情况.所以说,这里提到一个原则
|
|
|
|
>不管是封装也好,增强也好,不能丢失原有框架原本支持的特性.
|
|
|
|
那么.如果去掉上面两行的配置,大家运行项目,请求一个不而存在的地址,错误信息会变成如下情况:
|
|
|
|
``` java
|
|
{
|
|
"timestamp": 1503626878668,
|
|
"status": 404,
|
|
"error": "Not Found",
|
|
"message": "No message available",
|
|
"path": "/a/a/a"
|
|
}
|
|
```
|
|
|
|
而我们希望的错误输出格式是这样的:
|
|
|
|
``` java
|
|
{
|
|
"id": "e7593f09-2898-478e-a3bd-0280b93ac77f",
|
|
"code": "404",
|
|
"message": "Not Found",
|
|
"result": null,
|
|
"error": {
|
|
"date": 1503626920897,
|
|
"type": "......",
|
|
"message": ".........",
|
|
"stackTrace": null,
|
|
"child": null
|
|
}
|
|
}
|
|
```
|
|
|
|
如何调整呢?
|
|
|
|
## **源码解读**
|
|
|
|
在spring中,专门处理error的类是**BasicErrorController**,如下是这个类的两个核心方法,
|
|
|
|
一个方法处理html请求的返回,会默认返回一个错误页面
|
|
|
|
另外一个则是处理json请求的,会返回json格式的错误信息,我们看到的默认输出结果,就是这个方法生成的
|
|
|
|
``` java
|
|
@RequestMapping(produces = "text/html")
|
|
public ModelAndView errorHtml(HttpServletRequest request,
|
|
HttpServletResponse response) {
|
|
HttpStatus status = getStatus(request);
|
|
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
|
|
request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
|
|
response.setStatus(status.value());
|
|
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
|
|
return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
|
|
}
|
|
|
|
@RequestMapping
|
|
@ResponseBody
|
|
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
|
|
Map<String, Object> body = getErrorAttributes(request,
|
|
isIncludeStackTrace(request, MediaType.ALL));
|
|
HttpStatus status = getStatus(request);
|
|
return new ResponseEntity<Map<String, Object>>(body, status);
|
|
}
|
|
```
|
|
|
|
我们现在知道了我们要关注的地方,但是我们如何改造它呢?
|
|
|
|
细心的spring早就想到了可能存在的客制化需求,所以已经为我们留好了口子.
|
|
|
|
在源文件里**ErrorMvcAutoConfiguration**是用来配置**BasicErrorController**的,如下是核心方法,
|
|
|
|
这个方法上面标记了@ConditionalOnMissingBean,也就是说我们只需要实现一个ErrorController接口,注入到上下文中就可以了
|
|
|
|
>@ConditionalOnMissingBean的意思 : 当上下文中存在某一个bean,则不初始化当前被标记的bean
|
|
|
|
``` java
|
|
@Bean
|
|
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
|
|
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
|
|
return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
|
|
this.errorViewResolvers);
|
|
}
|
|
```
|
|
|
|
知道了方法,下面就开干吧!
|
|
|
|
## **实现**
|
|
|
|
创建一个类ExceptionController继承至AbstractErrorController(此基类实现了ErrorController)
|
|
|
|
``` java
|
|
@ApiIgnore
|
|
@Controller
|
|
@RequestMapping("${server.error.path:${error.path:/error}}")
|
|
@Slf4j
|
|
public class ExceptionController extends AbstractErrorController {
|
|
....省略
|
|
}
|
|
```
|
|
|
|
编写如下两个方法,其实就是跟默认实现的方法一样,只是内容可以定制
|
|
|
|
然后类中其他的方法,就按照默认的实现照抄就行了.
|
|
|
|
``` java
|
|
@RequestMapping(produces = "text/html")
|
|
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
|
|
HttpStatus status = getStatus(request);
|
|
Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
|
|
RestResponse<String> restResponse = this.getRestResponse(request, status, model);
|
|
model.put("restResponse", restResponse);
|
|
model.put(KEY_EXCEPTION, restResponse.getError().getType());
|
|
model.put(KEY_MESSAGE, restResponse.getError().getMessage());
|
|
response.setStatus(status.value());
|
|
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
|
|
return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
|
|
}
|
|
|
|
@RequestMapping
|
|
@ResponseBody
|
|
public ResponseEntity<RestResponse<String>> error(HttpServletRequest request) {
|
|
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
|
|
HttpStatus status = getStatus(request);
|
|
//这里构建自己的输出格式,详细代码就不贴出,如有需要可以到代码仓库查看
|
|
return new ResponseEntity<>(getRestResponse(request, status, body), status);
|
|
}
|
|
```
|
|
|
|
然后在创建ExceptionControllerConfig类,注意上面一堆注解照抄默认实现即可
|
|
|
|
``` java
|
|
@Configuration
|
|
@ConditionalOnWebApplication
|
|
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
|
|
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
|
|
@EnableConfigurationProperties(ResourceProperties.class)
|
|
public class ExceptionControllerConfig {
|
|
....省略
|
|
}
|
|
```
|
|
|
|
创建exceptionController的bean,然后到这里,我们就已经替换了spring boot默认的错误处理controller,后续的错误处理都会走ExceptionController类
|
|
|
|
``` java
|
|
@Bean
|
|
public ExceptionController exceptionController(ErrorAttributes errorAttributes) {
|
|
return new ExceptionController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
|
|
}
|
|
```
|
|
|
|
## **结束**
|
|
|
|
在上面我们优化了404错误的处理流程,替换掉了原来不是很健壮的处理方式,使得代码更加灵活,也更加合理了.
|
|
|
|
在下一篇文章中,我会介绍一下服务间接口调用的认证和鉴权的思考和设计.
|
|
|
|
## **代码仓库** (博客配套代码)
|
|
|
|
- [udf-starter : 基础项目,脚手架,框架](https://gitee.com/wangkang/udf)
|
|
|
|
- [udf-sample : 集成样例](https://gitee.com/wangkang/udf-sample)
|
|
|
|
---------
|
|
|
|
想获得最快更新,请关注公众号
|
|
|
|

|
|
|