効果のデモンストレーション
最初にSpringboot
デフォルトの効果を見てみましょう
ブラウザアクセス
クライアントアクセス
フォーカス!!!
しかし、会社のコードのほとんどは適応的に処理されていません。理由の大部分は、インターネットで検索してそのSpringboot全局异常处理
ようなコードを見つけたためです。
@ControllerAdvice
public class MyControllerAdvice {
@ResponseBody
@ExceptionHandler(value = Exception.class)
public ResponseEntity<?> errorHandler(Exception ex) {
// 处理异常
}
}
最初に通常の検索エンジンで検索してから、会社のコードを調べて、このコードに類似しているかどうかを確認してから、下を確認することを強くお勧めします。
もちろん、多くの学生は、私たちがクライアントと良好な合意をしたと言うかもしれません、そして、あるだけですがjson
、リターンhtml
シナリオはありません。したがって、適応しなくても問題ありません。ただし、インフラストラクチャを使用している学生の場合は、会社全体のビジネス部門を接続しているため、この機能を実行する必要があります。Springbootで実行できます。同様の基本コンポーネントを実行できます。機能がSpringbootよりも悪い場合は、ビジネスクラスメートはどう思いますか?
もちろん、ほとんどの学生にとって、それをしないことは大きな問題ではありません。
しかし、この方法では、良い学習の機会を逃してしまいます。どのような学習の機会ですか?多くのクラスメートは、インタビューでロケットを作るといつも言っているので、職場で理解できない問題が発生した場合は、BaiduまたはGoogleをクリックするだけです。ただし、この問題はあまりよく検索されていません。言い換えれば、ほとんどの人は検索エンジン用にプログラミングしています。検索エンジンで解決できない問題に遭遇したとき、それはあなたが自分の価値を反映し、それを大切にするときです。
この機能を実行することは重要ではないと思います。この問題解決能力を発揮する貴重な機会は非常にまれです。結局のところ、ほとんどの機能は簡単な検索やフェイチャオ交換グループでの質問によって解決できるのは事実です。
適応原理
多くの学生は、それを見つけることができなかったので、決定的にソースコードの波から始めたと言いました。ただし、Springboot
ソースコードが非常に多い場合、どこから始めればよいですか?これがポイントです!現時点では、公式文書の波を歩くことができます。
27.1.9エラー処理
Spring Boot provides an /error mapping by default that handles all errors in a sensible way, and it is registered as a ‘global’ error page in the servlet container. For machine clients it will produce a JSON response with details of the error, the HTTP status and the exception message. For browser clients there is a ‘whitelabel’ error view that renders the same data in HTML format (to customize it just add a View that resolves to ‘error’). To replace the default behaviour completely you can implement ErrorController and register a bean definition of that type, or simply add a bean of type ErrorAttributes to use the existing mechanism but replace the contents.
一些同学说,英文看不懂?我挑5个重点单词给你,都是小学单词,只要小学能毕业,我认为都能看懂。
clients JSON browser HTML ErrorController
肥朝小声逼逼:这里特别强调,并不是说看官方文档是最优解决问题方案。还是那句话,老司机都是看菜吃饭
的,解决问题的套路有很多,时间有限,我就不把所有套路一个一个列出来(其实是怕套路全部告诉你们了,你们就取关了!),直入主题就行。
从文档和小学的英文单词我们把目标锁定在了ErrorController
,给大家看一下关键代码
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
@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) ? modelAndView : new ModelAndView("error", model);
}
@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);
}
}
从这里我们大致可以猜测出,要做一个自适应的全局异常处理,理论上是要这么写的。
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(Exception.class)
public String handleExceptionHtml(Exception e, HttpServletRequest httpServletRequest) {
// 这里做一些你自己的处理,比如
httpServletRequest.setAttribute("欢迎关注微信公众号","肥朝");
return "forward:/error";
}
}
果然调试一波,发现果真如此。当然具体怎么自定义这个错误界面之类的,网上一搜就有,所以这些不是肥朝的重点。那么这个自适应全局异常似乎美滋滋了?
遇到问题
我们知道了这个自适应的全局异常处理的原理,也很容易想到怎么弄出bug。比如,你在拦截器出现了异常的话。
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
throw new RuntimeException("这里假装抛出一个肥朝异常");
//return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
});
}
}
那么就会出现,StackOverflowError
。
因为拦截器出现异常,会掉进你的全局异常处理,然后你的全局异常处理,又进行forward
,又进入了拦截器,然后一直循环。
那么怎么解决这个问题呢?我们见招拆招,这个时候,我要演示常见的几种不优雅
,但是平时大家都容易做的写法。
将配置写死
registry.addInterceptor(new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
throw new RuntimeException("这里假装抛出一个肥朝异常");
//return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}).excludePathPatterns("/error");
我们从
@RequestMapping("${server.error.path:${error.path:/error}}")
这里得知,这个error.path
是可以配置的,很多同学图快,excludePathPatterns
处写死了/error
,这样一直用默认的自然没问题,一旦人家配置了error.path
,就出问题了。
潜规则
这个潜规则的问题,是绝大部分同学写代码中最常见的问题。你想一下,你的自适应全局异常是解决了,但是,带来的影响却是,每一个拦截器都要加上excludePathPatterns
这么一个配置。对于使用者来说,这个必须加上某个配置,就是一种潜规则,而且,对于新来的同事而言,他根本不知道这种潜规则
,一旦潜规则
的代码多了,后续很难维护。
拓展思考
那么不潜规则的代码应该是怎么样的?
もちろん、多くの場合、私たちは暗黙のルールを持っている必要があります!たとえば、ビッグデータの学生は、送信されるログにアプリケーション名が必要です。次に、ビジネス側では、アプリケーション名を構成する必要があります。それで、ビジネスの同僚にこの暗黙のルールを知らせる方法。もちろん、多くの学生が言った後、同僚に直接特定のパラメータを追加するように伝えます。フェイチャオの毎日のつぶやきさえ覚えていません。すべての同僚が覚えていることを保証できますか?
要約すると、次のようなタイプの問題が発生しました。
1.この記事のように、インターセプターのいくつかの暗黙のルールパラメーターを設定する必要があります。どのように暗黙のルールをエレガントにするのですか?
2.たとえば、インターセプターには順序要件があります。たとえば、基本的なフレームワークでtraceInterceptor
インターセプターを定義する場合、このインターセプターを最初に配置する必要があります。したがって、問題は、これが最初であることをどのように確認するかです。何人かのクラスメートがちょうど言った、それから私は@Order
コントロールを使う。それから私はあなたのようなインターセプターを書きました、と呼ばれますfeichaoInterceptor
、コードはあなたのものとまったく同じです、それはまったく同じなので、どうすればあなたが私の前にいることを確認できますか?
3.暗黙のルールが必要なシーンでは、抵抗している場合でも、どのようにしてそれに忍び込むことができますか?
これらの質問には次号でお答えします。