springCloud系列:FeignClient 客户端捕获到服务端抛出的自定义异常

一、背景
        微服务架构中服务间相互调用,客户端会接收到的是一个由服务端返回的Response。使用FeignClient 调用时,例:A服务调用B服务,B服务处理业务时产生了异常,A服务无法获取到的该异常,只能获取到 FeignException 异常。

        我的业务场景是,有一个下发功能会调用一个http的外部接口, 前端先调用E服务,在E服务中调用B服务(系统所有的外部接口都在B服务),B服务调用外部接口 产生了一系列异常,E服务需要接收 B服务调用外部接口时产生的异常。

二、业务代码

1、被调用方 B服务

B服务的业务代码中抛出了自定义异常 DjqException:

private JSONObject http(HttpRequestBase http, Header[] headers, String url) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        http.setHeaders(headers);
        try {
            CloseableHttpResponse response = httpClient.execute(http);
            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            String entity = EntityUtils.toString(response.getEntity());
            log.info("大家签:" + url + " {}", entity);
            JSONObject jsonObject = JSON.parseObject(entity);
            if (HttpStatus.OK.value() == statusCode) {
                return jsonObject;
            }
            // TODO liuyong,接口调用失败,这里可能的原因是clientId或secret不正确
            throw new DjqException(jsonObject.getString("message"));
        } catch (IOException e) {
            e.printStackTrace();
            // TODO liuyong,接口调用失败,这里可能的原因是网络问题
            throw new DjqException("调用大家签接口失败");
        }
    }

DjqException 自定义异常类:

package cn.com.ebidding.base.djq.exception;

import lombok.Data;

/**
 * @Description: 适用于大家签接口抛出异常
 * @Date: 2024/2/29
 */
@Data
public class DjqException extends RuntimeException {

    /**
     * 错误码
     */
    private int code;

    /**
     * 错误信息
     */
    private String msg;

    public DjqException(String message) {
        this.msg = message;
    }

    @Deprecated
    public DjqException(String code, String message) {
        super(message);
        this.code = Integer.valueOf(code);
    }

    public DjqException(int code, String message) {
        super(message);
        this.code = code;
    }
}

自定义异常的转换类:

把异常类抛出的信息放到response中,response给一个指定的状态 518

package cn.com.ebidding.base.djq.exception;

import cn.com.ebidding.common.enums.DjqErrorHttpStatus;
import cn.com.ebidding.common.tool.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;

/**
 * @Description: 大家签接口产生的异常处理
 * @Date: 2024/2/29
 */
@RestControllerAdvice
@Slf4j
public class DjqExceptionHandler {

    /**
     * @Description: 捕获大家签接口产生的异常
     * @Date: 10:07 2024/2/29
     * @Param: DjqException
     */
    @ExceptionHandler(value = DjqException.class)
    public void djqExceptionHandler (HttpServletResponse response, DjqException ex) {
        response.setStatus(DjqErrorHttpStatus.JDQ_ERROR.value());
        response.addHeader("code", String.valueOf(ex.getCode()));
        String msg = "";
        try {
            msg = java.net.URLEncoder.encode(ex.getMsg(), "utf-8");
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage());
        }
        response.addHeader("msg", msg);
    }
}
/**
 * @Description:
 * @Date: 2024/2/29
 */
public enum DjqErrorHttpStatus {

    JDQ_ERROR(518, "DJQ Business error");

    private final int value;

    private final String reasonPhrase;

    DjqErrorHttpStatus(int value, String reasonPhrase) {
        this.value = value;
        this.reasonPhrase = reasonPhrase;
    }
    public int value() {
        return this.value;
    }
}

2、调用方 E服务

实现了feignClient的 ErrorDecoder 接口

捕获调用返回的response,通过返回的状态值判断是不是 前面我们封装的异常信息

正常抛出异常 DjqException、Exception就可以了

我是抛给了全局的自定义异常 EbsException ,系统会自动把全局异常封装给接口返回前端

/**
 * @Description: 捕获feign调用 大家签异常
 * @Date: 2024/2/29
 */
@Configuration
public class FeignErrorDecoder implements ErrorDecoder {

    @Override
    public EbsException decode(String methodKey, Response response) {
        int status = response.status();
        // 判断是否是接口抛出的状态值 518
        if (DjqErrorHttpStatus.JDQ_ERROR.value() == status) {
            // 这里直接拿到我们抛出的异常信息
            Map<String, Collection<String>> headers = response.headers();
            String message = headers.get("msg").iterator().next();
            try {
                String decode = URLDecoder.decode(message, "utf-8");
                return new EbsException(decode);
            } catch (UnsupportedEncodingException e) {
                return new EbsException("系统异常,请联系系统开发人员进行处理! ");
            }
        }
        return new EbsException("系统异常,请联系系统开发人员进行处理! ");
    }
}

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
如果`doGetAuthenticationInfo`抛出的是一个自定义异常,并且该异常没有被捕获,那么它会沿着调用栈一直抛出,直到被JVM捕获或导致应用程序崩溃。为了避免这种情况,可以使用全局异常处理器来捕获并处理`doGetAuthenticationInfo`抛出自定义异常。 在Java,可以通过实现`javax.servlet.Filter`接口并在`web.xml`文件配置来实现全局异常处理器。例如: ```java public class GlobalExceptionHandler implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { try { filterChain.doFilter(servletRequest, servletResponse); } catch (MyException e) { // 处理自定义异常 e.printStackTrace(); } catch (Exception e) { // 处理其他异常 e.printStackTrace(); } } // 省略其他法 } ``` 在`doFilter`,可以使用try-catch块来捕获自定义异常(例如`MyException`),并进行相应的处理。如果该异常没有被捕获,将会继续抛出,直到被全局异常处理器捕获。 然后,在`web.xml`文件配置全局异常处理器: ```xml <filter> <filter-name>GlobalExceptionHandler</filter-name> <filter-class>com.example.GlobalExceptionHandler</filter-class> </filter> <filter-mapping> <filter-name>GlobalExceptionHandler</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ``` 上述配置将会拦截所有的请求,并使用`GlobalExceptionHandler`来处理异常。这样,当`doGetAuthenticationInfo`抛出自定义异常时,就能够被全局异常处理器捕获并处理,而不会导致应用程序崩溃。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值