当前位置:   article > 正文

Feign异常捕获_feign捕获异常

feign捕获异常

目录

说明

准备

代码准备

测试


说明

分布式系统中,系统与系统之间通常使用FeignClient进行数据交互,但当被调用方需要抛出特定异常时,调用方不能捕获到异常信息进行业务处理,本文档对此种现象进行了说明,并提供一种解决方案。

准备

代码准备

client

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3.    <artifactId>spring-cloud-starter-openfeign</artifactId>
  4. </dependency>
  1. @FeignClient(name = "service-a", contextId = "service-client-a", url = "${feign.service.url}", path = "/test")
  2. public interface TestApi {
  3.    @GetMapping("/no-throws-ex")
  4.    String noThrowsEx();
  5.    @GetMapping("/has-throws-ex")
  6.    String hasThrowsEx(@RequestParam Integer code) throws SysEx;
  7. }
  1. @Getter
  2. @Builder
  3. public class SysEx extends RuntimeException{
  4.    private ResCode resCode;
  5.    public SysEx(ResCode resCode) {
  6.        super(resCode.getMsg());
  7.        this.resCode = resCode;
  8.   }
  9. }
  1. package org.example;
  2. import lombok.Getter;
  3. @Getter
  4. public enum  ResCode {
  5.    OK(0,"ok"),
  6.    E_500(500,"500的异常"),
  7.    E_1000(1000,"1000的异常"),
  8.    E_1001(1001,"1001的异常"),
  9.   ;
  10.    private int code;
  11.    private String msg;
  12.    ResCode(int code, String msg) {
  13.        this.code = code;
  14.        this.msg = msg;
  15.   }
  16.    public static ResCode ofCode(int code){
  17.        ResCode[] values = ResCode.values();
  18.        for (ResCode resCode : values){
  19.            if (code == resCode.code){
  20.                return resCode;
  21.           }
  22.       }
  23.        return ResCode.E_500;
  24.   }
  25. }
  1. package org.example;
  2. import lombok.Data;
  3. import lombok.experimental.Accessors;
  4. @Data
  5. @Accessors(chain = true)
  6. public class R<T> {
  7.    private int code;
  8.    private String msg;
  9.    private T data;
  10.    private boolean ok;
  11.    private R(int code, String msg, T data, boolean ok) {
  12.        this.code = code;
  13.        this.msg = msg;
  14.        this.data = data;
  15.        this.ok = ok;
  16.   }
  17.    public static <T> R<T> ok(T t){
  18.        return new R(ResCode.OK.getCode(), ResCode.OK.getMsg(), t, true);
  19.   }
  20.    public static <T> R<T> error(ResCode resCode){
  21.        return new R(resCode.getCode(), resCode.getMsg(), null, false);
  22.   }
  23. }

service 底层服务(被调用方) 提供2个测试接口,并对特定异常进行统一异常处理

  1. @GetMapping("/no-throws-ex")
  2. public ResponseEntity<String> noThrowsEx(){
  3.    return ResponseEntity.ok("noThrowsEx");
  4. }
  5. @GetMapping("/has-throws-ex")
  6. public ResponseEntity<String> hasThrowsEx(@RequestParam Integer code){
  7.    switch (code){
  8.        case 1:
  9.            throw new SysEx(ResCode.E_1000);
  10.        default:
  11.            return ResponseEntity.ok("hasThrowsEx");
  12.   }
  13. }
  1. @ExceptionHandler(SysEx.class)
  2. public ResponseEntity<R<?>> sysEx(SysEx e){
  3.    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(R.error(e.getResCode()));
  4. }

api 调用方 接口调用service服务中的方法

  1. @Autowired
  2. private TestApi testApi;
  3. @GetMapping("/test")
  4. public ResponseEntity<String> test(@RequestParam Integer code) {
  5.    switch (code){
  6.        case 1:
  7.        case 2:
  8.            try {
  9.                String s = testApi.hasThrowsEx(code);
  10.                return ResponseEntity.ok(s);
  11.           } catch (SysEx e){
  12.                log.error(e.getResCode());
  13.                return ResponseEntity.ok("抛出异常");
  14.           }
  15.        case 3:
  16.            return ResponseEntity.ok(testApi.noThrowsEx());
  17.        default:
  18.            return ResponseEntity.ok("异常");
  19.   }
  20. }
  1.    @ExceptionHandler(Exception.class)
  2.    public ResponseEntity<String> runtimeEx(Exception e){
  3.        return ResponseEntity.status(500).body("error");
  4.   }
  5.    @ExceptionHandler(SysEx.class)
  6.    public ResponseEntity<R<?>> sysEx(SysEx e){
  7.        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(R.error(e.getResCode()));
  8.   }

 

测试

  1. 在浏览器中访问接口http://127.0.0.1:9003/test/test?code=3返回数据noThrowsEx证明服务之间数据交互正常;

  2. 在浏览器中访问接口http://127.0.0.1:9003/test/test?code=2返回数据hasThrowsEx,接口hasThrowsEx未抛出异常,正常返回;

  3. 在浏览器中访问接口http://127.0.0.1:9003/test/test?code=1,由于调用方也存在异常处理机制,当出现Exception时,则返回数据error;证明当code=1时,服务抛出异常,导致系统出现错误,未能响应数据抛出异常。此时当在浏览器中访问接口http://127.0.0.1:9001/test/has-throws-ex?code=1时,由于服务中统一异常处理机制,返回数据为:

    1. {
    2.    "code": 1000,
    3.    "msg": "1000的异常",
    4.    "data": null,
    5.    "ok": false
    6. }

    由上述几次测试可知,当底层服务出现异常时,上层服务不能正确的捕获底层服务抛出的异常信息,并出现了500的错误;且未能将底层服务相关提示信息展示到上层服务,这种现象对于上层调用方极其不友好,下面对client中代码进行了改造:

    TestApi

@FeignClient(name = "service-a", contextId = "service-client-a", url = "${feign.service.url}", path = "/test",fallback = ErrorFallback.class)

在代码中添加了fallback = ErrorFallback.class

ErrorFallback

  1. package org.example;
  2. import cn.hutool.json.JSONUtil;
  3. import feign.Response;
  4. import feign.codec.ErrorDecoder;
  5. import lombok.extern.log4j.Log4j2;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.http.HttpStatus;
  8. import org.springframework.util.StreamUtils;
  9. import java.io.IOException;
  10. import java.nio.charset.Charset;
  11. @Configuration
  12. @Log4j2
  13. public class ErrorFallback implements ErrorDecoder {
  14.    @Override
  15.    public Exception decode(String s, Response response) {
  16.        if (response.status() != HttpStatus.OK.value()){
  17.            log.info("s: {}",s);
  18.            log.info("status: {}" ,response.status());
  19.            try {
  20.                String content = StreamUtils.copyToString(response.body().asInputStream(), Charset.defaultCharset());
  21.                log.info("content: {}", content);
  22.                R r = JSONUtil.toBean(content, R.class);
  23.                return new SysEx(ResCode.ofCode(r.getCode()));
  24.           } catch (IOException e) {
  25.                e.printStackTrace();
  26.           }
  27.       }
  28.        return new SysEx(ResCode.E_500);
  29.   }
  30. }

重新启动服务后,访问接口http://127.0.0.1:9003/test/test?code=1,返回数据为抛出异常,证明

ErrorFallback配置成功

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/84606
推荐阅读
相关标签
  

闽ICP备14008679号