欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解SpringCloud Finchley Gateway 统一异常处理

程序员文章站 2024-03-05 22:52:49
springcloud finchley gateway 统一异常处理 全文搜索[@@]搜索重点内容标记 1 . 问题:使用springcloud gateway时,会...

springcloud finchley gateway 统一异常处理

全文搜索[@@]搜索重点内容标记

1 . 问题:使用springcloud gateway时,会出现各种系统级异常,默认返回html.

2 . finchley版本的gateway,使用webflux形式作为底层框架,而不是servlet容器,所以常规的异常处理无法使用

翻阅源码,默认是使用defaulterrorwebexceptionhandler这个类实现结构如下:

详解SpringCloud Finchley Gateway 统一异常处理

可以实现参考defaulterrorwebexceptionhandlerabstracterrorwebexceptionhandler自定义实现errorwebexceptionhandler,然后,注册为bean到spring容器中即可(bean name:"errorwebexceptionhandler"

具体实现代码如下:

package pro.chenggang.example.spring.cloud.gateway.support;

import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.boot.web.reactive.error.errorwebexceptionhandler;
import org.springframework.cloud.gateway.support.notfoundexception;
import org.springframework.http.httpstatus;
import org.springframework.http.mediatype;
import org.springframework.http.codec.httpmessagereader;
import org.springframework.http.codec.httpmessagewriter;
import org.springframework.http.server.reactive.serverhttprequest;
import org.springframework.util.assert;
import org.springframework.web.reactive.function.bodyinserters;
import org.springframework.web.reactive.function.server.requestpredicates;
import org.springframework.web.reactive.function.server.routerfunctions;
import org.springframework.web.reactive.function.server.serverrequest;
import org.springframework.web.reactive.function.server.serverresponse;
import org.springframework.web.reactive.result.view.viewresolver;
import org.springframework.web.server.responsestatusexception;
import org.springframework.web.server.serverwebexchange;
import reactor.core.publisher.mono;

import java.util.collections;
import java.util.hashmap;
import java.util.list;
import java.util.map;

/**
 * @classdesc: 统一异常处理,参考{@link org.springframework.web.server.abstracterrorwebexceptionhandler}修改
 * @author: chenggang
 * @createtime: 2018/10/30
 */
public class jsonexceptionhandler implements errorwebexceptionhandler {

 private static final logger log = loggerfactory.getlogger(jsonexceptionhandler.class);

 /**
  * messagereader
  */
 private list<httpmessagereader<?>> messagereaders = collections.emptylist();

 /**
  * messagewriter
  */
 private list<httpmessagewriter<?>> messagewriters = collections.emptylist();

 /**
  * viewresolvers
  */
 private list<viewresolver> viewresolvers = collections.emptylist();

 /**
  * 存储处理异常后的信息
  */
 private threadlocal<map<string,object>> exceptionhandlerresult = new threadlocal<>();

 /**
  * 参考abstracterrorwebexceptionhandler
  * @param messagereaders
  */
 public void setmessagereaders(list<httpmessagereader<?>> messagereaders) {
  assert.notnull(messagereaders, "'messagereaders' must not be null");
  this.messagereaders = messagereaders;
 }

 /**
  * 参考abstracterrorwebexceptionhandler
  * @param viewresolvers
  */
 public void setviewresolvers(list<viewresolver> viewresolvers) {
  this.viewresolvers = viewresolvers;
 }

 /**
  * 参考abstracterrorwebexceptionhandler
  * @param messagewriters
  */
 public void setmessagewriters(list<httpmessagewriter<?>> messagewriters) {
  assert.notnull(messagewriters, "'messagewriters' must not be null");
  this.messagewriters = messagewriters;
 }

 @override
 public mono<void> handle(serverwebexchange exchange, throwable ex) {
  /**
   * 按照异常类型进行处理
   */
  httpstatus httpstatus;
  string body;
  if (ex instanceof notfoundexception) {
   httpstatus = httpstatus.not_found;
   body = "service not found";
  }else if(ex instanceof responsestatusexception) {
   responsestatusexception responsestatusexception = (responsestatusexception) ex;
   httpstatus = responsestatusexception.getstatus();
   body = responsestatusexception.getmessage();
  }else{
   httpstatus = httpstatus.internal_server_error;
   body ="internal server error";
  }
  /**
   * 封装响应体,此body可修改为自己的jsonbody
   */
  map<string,object> result = new hashmap<>(2,1);
  result.put("httpstatus",httpstatus);
  result.put("body",body);
  /**
   * 错误记录
   */
  serverhttprequest request = exchange.getrequest();
  log.error("[全局异常处理]异常请求路径:{},记录异常信息:{}",request.getpath(),ex.getmessage());
  /**
   * 参考abstracterrorwebexceptionhandler
   */
  if (exchange.getresponse().iscommitted()) {
   return mono.error(ex);
  }
  exceptionhandlerresult.set(result);
  serverrequest newrequest = serverrequest.create(exchange, this.messagereaders);
  return routerfunctions.route(requestpredicates.all(), this::rendererrorresponse).route(newrequest)
    .switchifempty(mono.error(ex))
    .flatmap((handler) -> handler.handle(newrequest))
    .flatmap((response) -> write(exchange, response));

 }

 /**
  * 参考defaulterrorwebexceptionhandler
  * @param request
  * @return
  */
 protected mono<serverresponse> rendererrorresponse(serverrequest request) {
  map<string,object> result = exceptionhandlerresult.get();
  return serverresponse.status((httpstatus) result.get("httpstatus"))
    .contenttype(mediatype.application_json_utf8)
    .body(bodyinserters.fromobject(result.get("body")));
 }

 /**
  * 参考abstracterrorwebexceptionhandler
  * @param exchange
  * @param response
  * @return
  */
 private mono<? extends void> write(serverwebexchange exchange,
          serverresponse response) {
  exchange.getresponse().getheaders()
    .setcontenttype(response.headers().getcontenttype());
  return response.writeto(exchange, new responsecontext());
 }

 /**
  * 参考abstracterrorwebexceptionhandler
  */
 private class responsecontext implements serverresponse.context {

  @override
  public list<httpmessagewriter<?>> messagewriters() {
   return jsonexceptionhandler.this.messagewriters;
  }

  @override
  public list<viewresolver> viewresolvers() {
   return jsonexceptionhandler.this.viewresolvers;
  }

 }
}

注册bean

 /**
  * 自定义异常处理[@@]注册bean时依赖的bean,会从容器中直接获取,所以直接注入即可
  * @param viewresolversprovider
  * @param servercodecconfigurer
  * @return
  */
 @primary
 @bean
 @order(ordered.highest_precedence)
 public errorwebexceptionhandler errorwebexceptionhandler(objectprovider<list<viewresolver>> viewresolversprovider,
                servercodecconfigurer servercodecconfigurer) {

  jsonexceptionhandler jsonexceptionhandler = new jsonexceptionhandler();
  jsonexceptionhandler.setviewresolvers(viewresolversprovider.getifavailable(collections::emptylist));
  jsonexceptionhandler.setmessagewriters(servercodecconfigurer.getwriters());
  jsonexceptionhandler.setmessagereaders(servercodecconfigurer.getreaders());
  log.debug("init json exception handler instead default errorwebexceptionhandler success");
  return jsonexceptionhandler;
 }

[@@]注意事项:

1 .上面为示例代码,其中牵扯到策略工厂和响应封装的类,可以自定义实现

2 .注册bean时依赖的bean,都会从spring容器中获取到

3 .参考此方法思路,可实现统一异常处理,统一封装错误信息。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。