Spring Cloud 异常处理和状态码

异常处理和状态码

一、自定义异常类

OrderNotFoundException.java

@ResponseStatus(HttpStatus.NOT_FOUND)
public class OrderNotFoundException extends RuntimeException {
    public OrderNotFoundException(String message) {
        super(message);
    }
}
@RequestMapping(value = "/trade/order", method = RequestMethod.GET)
public Order doGet(@RequestParam Long id) {
  if (id == 1L) {
      Order order = new Order();
      order.setId(1L);
      order.setCustomerId(1L);
      order.setPrice(12.32);
      order.setSellerId(2L);
      return order;
  } else {
      throw new OrderNotFoundException("订单不存在");
  }
}

curl http://localhost:8080/trade/order?id=1

{
  "id": 1,
  "price": 12.32,
  "customerId": 1,
  "itemId": null,
  "sellerId": 2
}

curl http://localhost:8080/trade/order?id=2

{
  "timestamp": 1485078385014,
  "status": 404,
  "error": "Not Found",
  "exception": "com.erdaoya.springcloud.trade.entity.OrderNotFoundException",
  "message": "订单不存在",
  "path": "/trade/order"
}

问题:

直接返回给客户端,暴露了异常信息,其实只需要message

[
  {
    "id": 1,
    "price": 12.32,
    "customerId": 1,
    "itemId": null,
    "sellerId": 2
  },
  {
    "id": 2,
    "price": 12.32,
    "customerId": 1,
    "itemId": null,
    "sellerId": 2
  }
]

二、在网关包一个层级

这个输入直接返回给前端没问题,但如果想做自己定制,可以统一在Zuul的filter中进行

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseModel<T> {
    // 返回消息
    private String message = "";
    // 返回对象
    private T data;
    // debug模式,需要在配置开关,开发测试模式可以开启
    private T error;
    private String debug;
    public ResponseModel() {
    }
    public ResponseModel(T data) {
            this.data = data;
    }
    public ResponseModel(String message) {
        if (null != message) {
            this.message = message;
        }
    }
    public void setMessage(String message) {
        if (null != message) {
            this.message = message;
        }
    }
    public void setData(T data) {
        if (null != data) {
            this.data = data;
        }
    }
    public void setError(T error){
        if (null != error){
            this.error =  error;
        }
    }
}
@Slf4j
public class ResponseFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return "post";
    }
    @Override
    public int filterOrder() {
        return 0;
    }
    @Override
    public boolean shouldFilter() {
        return true;
    }
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        try (InputStream responseDataStream = ctx.getResponseDataStream()) {
            final String responseData = CharStreams.toString(new InputStreamReader(responseDataStream, "UTF-8"));
            int responseStatusCode = ctx.getResponseStatusCode();

            ResponseModel<Object> response = new ResponseModel<>();
            ObjectMapper mapper = new ObjectMapper();

            JsonNode jsonNode = mapper.readTree(responseData);

            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

            if (responseStatusCode >= 400 && responseStatusCode < 500) {
                JsonNode message = jsonNode.get("message");
                if (message == null) {
                    response.setMessage("");
                } else {
                    response.setMessage(message.textValue());
                }
                response.setData(new Object());
            } else {
                response.setData(jsonNode);
            }
            ctx.setResponseBody(mapper.writeValueAsString(response));

        } catch (IOException e) {
            log.warn("Error reading body", e);
        }
        return null;
    }
}

通过zuul代理后的结果: curl http://localhost:8081/trade/order?id=1

{
  "message": "",
  "data": {
    "id": 1,
    "price": 12.32,
    "customerId": 1,
    "itemId": null,
    "sellerId": 2
  }
}

curl http://localhost:8081/trade/order?id=2

{
    "message": "订单不存在",
    "data": {}
}

缺点:最外层协议和里层协议不一样,不好测试;

三、使用ResponseEntity

@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Order> doGet(@RequestParam Long id) {
    if (id == 1L) {
        return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(null);
    } else if (id == 2) {
        String str = null;
        str.length();
        return null;
    } else {
        Order order = new Order();
        order.setId(123L);
        order.setCustomerId(1L);
        order.setPrice(12.32);
        order.setSellerId(2L);
        return ResponseEntity.ok(order);
    }
}

curl http://localhost:8080/trade/order?id=1 返回体空,状态码422

curl http://localhost:8080/trade/order?id=2

{
  "timestamp": 1485162083015,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "java.lang.NullPointerException",
  "message": "No message available",
  "path": "/trade/order2"
}

curl http://localhost:8080/trade/order?id=3

{
  "id": 123,
  "price": 12.32,
  "customerId": 1,
  "itemId": null,
  "sellerId": 2
}
CONTENTS