利用springboot实现http mock 服务

实现原理:
1. springboot 搭建一个http restul service, 实现一个/error 接口(自定义的错误接口)
2. 当我们访问该服务时例如在 http://localhost:8080/api , 假设我们的springboot没有/api的requestmapping, 那么内部转到我们的自定义错误返回 /error, /error 的逻辑则去找对应的数据库中有没有定义/api 的mock 规则. 如果找到,则返回对应的mock 报文.

首先我们创建一个controller , implements ErrorController, 并实现

@Override
public String getErrorPath() {
log.info("get error path");
return "/error";
}

那么,当我们随意访问一个uri时,应该返回的404错误. 这时候我们让springboot服务, 遇到错误时, 自动跳转到访问 /error 接口.

如下是一个/error接口的实现. 首先判断404错误. 如果请求springboot第一次返回的是404错误,则表明当前服务没有此接口,则去mock规则去查询是否有配置的规则,如果有则获取到对应的mock报文并返回. 但是这里有一个问题是, /api 访问此接口的时候, response 返回的http 状态码是404. 此时springboot内部转到/error 接口. 此时我们在error接口中获取到了对应的mock报文,并想将response的http code 改为200 并返回. 但是 response(ResponseFacade 中的 org.apache.catalina.connector.Response 中的 org.apache.coyote.Response 的 setStatus方法实现, 当status>399 时,认为是系统错误. 此时setStatus并不会修改status的值. 通过这段代码,和实际在debug中的验证, 则发现,

1) /api (HttpServletRequest, HttpServletResponse) , 返回404后
2) /error (HttpServletRequest, HttpServletResponse) HttpServletResponse 中的值仍然是404, 此时手动setStatus 或者 通过 /error return ResponseEntity(设定其他的HttpStatus). 不会设置成功. 所以/error 也自然返回了 httpcode 404. 只是返回的body message 实现成了自定义.

备注: org.apache.coyote.Response的setStatus的源码片段如下.

// public void setStatus(int status) {
// if (this.status > 399) {
// // Don't overwrite first recorded error status
// return;
// }
// this.status = status;
// }

其他思考: 通过/error 返回exception , 通过exception Handler 的方式应该也可以自定义返回的http status code和报文. 但此时自定义http status code可能也有类似的情况,需要通过反射的方式来强制设定. (待验证)


@RequestMapping(value = "/error")
public String error(HttpServletRequest request, @RequestHeader Map headers,
HttpServletResponse response) {

Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());

if (statusCode == HttpStatus.NOT_FOUND.value()) {

// 此处的代码需要额外注意.
ResponseFacade responsefacade = (ResponseFacade) response;

try {

Field innerResponse = getField(responsefacade.getClass(), "response");
innerResponse.setAccessible(true);
Response innterResponseObject = (Response) innerResponse.get(responsefacade);
org.apache.coyote.Response coyoteResponse = innterResponseObject.getCoyoteResponse();

Field httpstatus = getField(coyoteResponse.getClass(), "status");
httpstatus.setAccessible(true);
httpstatus.set(coyoteResponse, 200);

} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return mockservice.getMockResponse(headers,
(String) request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI));

// return "{\"" +"404 error found!"+"\"}";
} else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "{\"" + "500 error found!" + "\"}";
}
}

return "{\"" + "This is the default error response!" + "\"}";
}

@RestController
public class ForwardController implements ErrorController {

@Autowired
MockserviceImpl mockservice;

@RequestMapping(value = "/error")
public String error(HttpServletRequest request, @RequestHeader Map headers,
HttpServletResponse response) {

Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());

if (statusCode == HttpStatus.NOT_FOUND.value()) {

//
ResponseFacade responsefacade = (ResponseFacade) response;

try {

Field innerResponse = getField(responsefacade.getClass(), "response");
innerResponse.setAccessible(true);
Response innterResponseObject = (Response) innerResponse.get(responsefacade);
org.apache.coyote.Response coyoteResponse = innterResponseObject.getCoyoteResponse();

Field httpstatus = getField(coyoteResponse.getClass(), "status");
httpstatus.setAccessible(true);
httpstatus.set(coyoteResponse, 200);

} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return mockservice.getMockResponse(headers,
(String) request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI));

// return "{\"" +"404 error found!"+"\"}";
} else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "{\"" + "500 error found!" + "\"}";
}
}

return "{\"" + "This is the default error response!" + "\"}";
}

@Override
public String getErrorPath() {
log.info("get error path");
return "/error";
}

private static Field getField(Class clazz, String fieldName) throws NoSuchFieldException {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
Class superClass = clazz.getSuperclass();
if (superClass == null) {
throw e;
} else {
return getField(superClass, fieldName);
}
}
}

}

此篇文章已被阅读1728 次

Add a Comment

邮箱地址不会被公开。 必填项已用*标注