项目地址:https://github.com/xiaogou446/jsonboot
使用branch:feature/adjustHttpResponse
命令行:git checkout feature/adjustHttpResponse
设置失败响应
在我们之前的实现中,不管系统是否成功处理请求,不管是否返回正确的结果,系统都会一致的返回数据,这并不能很好的反应当前系统执行的状况,也无法判断具体出现的问题。
为此我们需要对响应进行一层封装,区分处理成功的响应与失败的响应。如果请求处理成功,那我们直接返回数据即可,如果请求处理失败,那需要进行错误原因的记录,以及出现的异常。
/**
* 服务器处理异常时,返回的数据格式
*
* @author qinghuo
* @since 2021/03/23 13:34
*/
@Getter
@ToString
@NoArgsConstructor
public class ErrorResponse {
/**
* 状态码
*/
private int status;
/**
* 错误原因
*/
private String message;
/**
* 请求路径
*/
private String path;
/**
* 时间
*/
private String dateTime;
public ErrorResponse(int status, String message, String path) {
this.status = status;
this.message = message;
this.path = path;
this.dateTime = DateUtil.now();
}
}
/**
* 处理成功时返回的数据封装响应
*
* @param object 成功时返回的数据
* @return 封装的响应
*/
public static FullHttpResponse ok(Object object){
byte[] bytes = JACKSON_SERIALIZER.serialize(object);
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(bytes));
response.headers().set(CONTENT_TYPE, APPLICATION_JSON);
response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
return response;
}
/**
* 当请求处理失败时,生成失败响应
* @param uri 请求路径
* @return 响应
*/
public static FullHttpResponse internalServerError(String uri){
ErrorResponse errorResponse = new ErrorResponse(INTERNAL_SERVER_ERROR.code(), INTERNAL_SERVER_ERROR.reasonPhrase(), uri);
byte[] bytes = JACKSON_SERIALIZER.serialize(errorResponse);
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, INTERNAL_SERVER_ERROR, Unpooled.wrappedBuffer(bytes));
response.headers().set(CONTENT_TYPE, "application/text");
response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
return response;
}
在HttpRequestHandler,通过try-catch进行对结果对判断,成功生成返回数据则代表响应成功,如果期间出现异常,代表响应失败,生成失败对结果返回。
try{
Object result = requestHandler.handler(request);
response = HttpResponse.ok(result);
}catch (Exception e){
e.printStackTrace();
response = HttpResponse.internalServerError(uri);
}
使用branch:feature/setPostRequest
命令行:git checkout feature/setPostRequest
完成Post请求
在我们处理post请求时,一般都是通过 @RequestBody对请求携带的json数据进行转换成需要类型的数据。同时,post请求也一样会兼顾url上携带的参数数据,本次就对post请求进行一个实现。
定义一下 @RequestBody 注解
/**
* requestBody注解,适用于json格式的post请求
*
* @author qinghuo
* @since 2021/03/23 14:02
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
public @interface RequestBody {
}
重新设置Post请求的handler处理代码,前段代码与处理Get请求差不多,都需要提取path以及附带在uri上都参数,判断请求数据的类型,如果是Json格式,则对每个参数进行遍历。如果参数上有 @RequestBody注解,就通过objectMapper对类型进行转换,再通过反射参数赋值。如果是有 @RequestParam或者没有注解标的参数,使用uri短链上的参数进行适配。
@Override
public Object handler(FullHttpRequest fullHttpRequest) {
String uri = fullHttpRequest.uri();
//post请求也有可能在uri上使用参数
Map<String, String> queryParamMap = UrlUtil.getQueryParam(uri);
String path = UrlUtil.convertUriToPath(uri);
Method method = Route.postMethodMapping.get(path);
if (method == null){
return null;
}
Parameter[] parameters = method.getParameters();
String contentTypeStr = fullHttpRequest.headers().get(CONTENT_TYPE);
String contentType = contentTypeStr.split(";")[0];
if (StringUtils.isBlank(contentType)){
return null;
}
//设置参数值
List<Object> params = new ArrayList<>();
//设置传入的参数实体
String jsonContent = fullHttpRequest.content().toString(Charsets.toCharset(CharEncoding.UTF_8));
//如果是json格式 改造结构 在handler下设置annotation包 用作处理
if (StringUtils.equals(APPLICATION_JSON, contentType)){
for(Parameter parameter : parameters){
Object result = null;
RequestBody requestBody = parameter.getAnnotation(RequestBody.class);
//代表当前参数上有requestBody注解
if (requestBody != null){
try {
//通过objectMapper进行类型的转换
result = objectMapper.readValue(jsonContent, parameter.getType());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
} else {
//如果没有@RequestBody标注,则默认从uri短链上获取参数
RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
//获取每个参数的类型
Class<?> type = parameter.getType();
//定义最终参数
String paramValue;
//有注解的情况
if (requestParam != null){
//获取注解设置的value值
String paramKey = requestParam.value();
//以注解设置的value值为key去参数map中取出值
paramValue = queryParamMap.get(paramKey);
}else{
//如果没有注解,则直接进行名称对应查找
paramValue = queryParamMap.get(parameter.getName());
}
//获取到的数据进行转换类型
result = ObjectUtil.convertToClass(type, paramValue);
}
params.add(result);
}
}
//反射执行方法
return ReflectionUtil.executeMethod(method, params.toArray());
}
以上就比较粗略的完成了Post的方法,但是这种写法会出现一个比较严重的问题,由于我们post的处理方法中也用到了大量get请求的处理方式,则会导致代码的高度耦合,如果未来再使用一个注解,就需要两边改,显得麻烦且容易出错。下一节修改注解的处理结构并完成新的注解。
测试
如果出现异常,则会返回我们定义的异常格式
这节…不知不觉就水过去了…见谅