Bootstrap

基于Springboot用axiospost请求接收字符串参数为null的解决方案

问题

今天在用前端 post 请求后端时发现,由于是以 Json对象的形式传输的,后端用两个字符串形参无法获取到对应的参数值

前端代码如下:

axios.post('http://localhost:8083/test/postParams',{a: '1', b:'2'} ,{
        'Content-Type': 'application/json'
    }).then(response => {
      console.log(response.data);
    })
    .catch(error => {
      console.error('There was an error!', error);
});

后端代码如下:

@RequestMapping("/test")
@RestController
@Slf4j
public class TestController {
    @PostMapping("/postParams")
    public void postParams(String a, String b) {
        log.info(String.valueOf(a));
        log.info(b);
    }
}

image-20250123171531186

解决

在网上学习了一下,究其原因是Spring Boot 无法直接将 JSON 字符串转换为一个 String 变量, Spring Boot 需要通过相应的机制,将 JSON 字符串解析成可用的 Java 对象或 Map,在学习了某位前辈的文章后,通过自定义注解的方式解决了问题:

​ 总的思路就是,getRequestBody()将请求的json对象字符串先缓存到cache中,然后将该字符串解析成Json对象,在根据对应的方法形参的名字,将值注入进去。

自定义注解类

/**
 * @author yamu
 * @version 1.0
 * @description: 接收前端传的 包装类数据 或 String 自定义注解
 * @date 2025/1/13 11:05
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestJson {
    //参数值(对应的键名)
    String value() default "";
}

定义@RequestJson的方法形参解析器

/**
 * @author yamu
 * @version 1.0
 * @description: 自定义注解 RequestJson 方法形参解析器
 * @date 2025/1/13 11:07
 */
@Component
@Slf4j
public class RequestJsonMethodArgumentResolver implements HandlerMethodArgumentResolver {
    public static String cache = "";//缓存请求体
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestJson.class);
    }
    /**
     * @description  String 参数注入
     * @param: parameter
     * @param: mavContainer
     * @param: webRequest
     * @param: binderFactory
     * @returns Object
     * @author yamu
     * @date 2025/1/20 14:33
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        RequestJson requestJson = parameter.getParameterAnnotation(RequestJson.class);
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        //未指定映射的键时,默认值为形参名
        String value = requestJson.value();
        if (value.isEmpty()) {
            value = parameter.getParameterName();
        }
        JSONObject jsonObject = getRequestBody(request);
        //遍历完最后一个参数,则清理缓存
        if (parameter.getMethod().getParameterCount() - 1 <= parameter.getParameterIndex()) {
            cache = "";
        }
        //请求的参数为空,直接返回null
        if (jsonObject == null) {
            return null;
        }
        return jsonObject.get(value);
    }

    /**
     * 获取参数列表
     * @param request
     * @return
     */
    private JSONObject getRequestBody(HttpServletRequest request) {
        //cache不为空
        if (!cache.isEmpty()) {
            return JSONObject.parseObject(cache);
        }
        //字符串拼接成Json字符串
        StringBuilder sb = new StringBuilder();
        try {
            BufferedReader reader = request.getReader();
            char[] buf = new char[1024];
            int rd;
            while ((rd = reader.read(buf)) != -1) {
                sb.append(buf, 0, rd);
            }
        } catch (IOException ex) {
            log.error(ex.getMessage());
        }
        cache = sb.toString();
        return JSONObject.parseObject(sb.toString());
    }
}

在WebConfig里注册解析器

@Configuration
@Slf4j
public class WebConfig extends WebMvcConfigurationSupport  {

    @Autowired
    private RequestJsonMethodArgumentResolver requestJsonMethodArgumentResolver;

    /**
     * @description 配置方法解析器
     * @param: argumentResolvers
     * @returns void
     * @author yamu
     * @date 2025/1/23 16:00
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(requestJsonMethodArgumentResolver);
    }
}

在方法形参上加上注解

@RequestMapping("/test")
@RestController
@Slf4j
public class TestController {
    @PostMapping("/postParams")
    public void postParams(@RequestJson String a, @RequestJson String b) {
        log.info(a);
        log.info(b);
    }
}

image-20250123174734261

上述方式存在几个问题:

  1. 由于要缓存请求的 Json字符串,所以在每次请求完之后要清除cache,上述方法是在方法形参的最后一个并且加了@RequestJson注解的参数才可以清理
  2. 由于需要对每个参数进行赋值,所以需要对每个要注入的参数都要加上@RequestJson注解
  3. 处理包装类或字符串类时,形参类型需要强一致(不能用Stringl类型接收一个Integer的参数值),同时也无法处理复杂的对象类型

后续在逐渐的深入学习后我会优化上述方式。

;