@RequestParam、@RequestBody不够用?一起来看Springboot如何自定义参数解析器

一、使用场景

Springboot参数接收方式,提供了@RequestParam@RequestBody,还有@RequestAttribute@PathVariable。但是有的时候,接口入参只需要一个参数,如果将一个参数封装成一个对象显得有些麻烦且没有必要。我么可以通过自定义注解实现,参数的自动解析和赋值。

二、自定义注解解析接口入参

1. 添加自定义注解

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestSingleParam {
    String value();

    boolean required() default true;

    String defaultValue() default "";
}

2. 定义参数解析器

import com.alibaba.fastjson.JSONObject;
import com.keyou.evm.common.annotation.RequestSingleParam;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;

public class RequestSingleParamHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestSingleParam.class);
    }


    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        RequestSingleParam requestSingleParam = parameter.getParameterAnnotation(RequestSingleParam.class);
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        BufferedReader reader = request.getReader();
        StringBuilder sb = new StringBuilder();
        char[] buf = new char[1024];
        int rd;
        while ((rd = reader.read(buf)) != -1) {
            sb.append(buf, 0, rd);
        }
        JSONObject jsonObject = JSONObject.parseObject(sb.toString());
        String value = requestSingleParam.value();
        return jsonObject.get(value);
    }

}

3. Spring注入参数解析器

Spring5.x可以添加配置类:


import com.keyou.evm.common.Handler.RequestSingleParamHandlerMethodArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * 使系统加载jar包外的文件
 */
@Configuration
public class ParameterConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers){
        argumentResolvers.add(new RequestSingleParamHandlerMethodArgumentResolver());
    }
}

如果是Spring4.x以前的版本,则可以修改Springboot启动类:

  1. 继承WebMvcConfigurerAdapter;
  2. 重写addArgumentResolvers()方法。
@SpringBootApplication
@EnableWebMvc
public class SpringbootDemoApplication extends WebMvcConfigurerAdapter {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers){
        argumentResolvers.add(new RequestSingleParamHandlerMethodArgumentResolver());
        super.addArgumentResolvers(argumentResolvers);
    }
}

三、多个入参优化

基于7-1我们发现在使用的过程中,假如代码中存在第二个参数,则使用@RequestSingleParam就不能再次读取第二个参数了。读取第二个参数的时候会爆出空指针异常。

    @PostMapping("/roles/active")
    @ApiOperation(value = "用户切换角色")
    @LogAnnotation(module = "user-center", recordRequestParam = true)
    public CommonResult activeRole(@RequestSingleParam ("roleCode") String roleCode,@RequestSingleParam ("client") String client) throws ControllerException {
        int i = userCertificateService.activeRole(roleCode, client);
        if (i > 0) {
            return CommonResult.success("切换角色成功!");
        }
        return CommonResult.failed("切换角色失败!");
    }

根本原因

request请求流中数据只能读取一次。

我的解决方法

Request中的参数不能多次读取,但是Request还提供了一种参数方式Attribute,在请求传递的过程中,还可以添加或修改参数。所以基于这个原理,我们对RequestSingleParamHandlerMethodArgumentResolver代码进行了优化:

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.keyou.evm.common.annotation.RequestSingleParam;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;

public class RequestSingleParamHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestSingleParam.class);
    }


    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        RequestSingleParam requestSingleParam = parameter.getParameterAnnotation(RequestSingleParam.class);
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        String value = requestSingleParam.value();
        JSONObject jsonObject ;
        if (ObjectUtil.isNotEmpty(request.getAttribute("data"))){   //注意:入参不能为data
            jsonObject = (JSONObject) request.getAttribute("data");
        }else {
            BufferedReader reader = request.getReader();
            StringBuilder sb = new StringBuilder();
            char[] buf = new char[1024];
            int rd;
            while ((rd = reader.read(buf)) != -1) {
                sb.append(buf, 0, rd);
            }
            jsonObject = JSONObject.parseObject(sb.toString());
            request.setAttribute("data",jsonObject);
        }
        return jsonObject.get(value);
    }

}

四、使用方法

    @ApiOperation("检查是否是第一次接收消息")
    @PostMapping("/checkFirstByDay")
    public CommonResult<Object> checkFirstByDay(@RequestSingleParam("phone") String phone){
        boolean b = chatService.checkFirstByDay(phone);
        if (b) {
            return CommonResult.success(true,"消息发送成功");
        }else {
            return CommonResult.success(false,"24小时内已提醒过该用户");
        }
    }

五、参考资料

  1. SpringBoot 接收单个String入参之解决方案
  2. springboot自定义参数解析HandlerMethodArgumentResolver的使用
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子涵先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值