异常处理和状态码
一、自定义异常类
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
}