一起来写个SpringBoot[3] — — 完成Post请求

项目地址: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请求的处理方式,则会导致代码的高度耦合,如果未来再使用一个注解,就需要两边改,显得麻烦且容易出错。下一节修改注解的处理结构并完成新的注解

测试

在这里插入图片描述
在这里插入图片描述
如果出现异常,则会返回我们定义的异常格式
在这里插入图片描述

这节…不知不觉就水过去了…见谅

下一节:一起来写个SpringBoot[4] — — 实现@PathVariable注解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值