由于spring中没有提供类似于@RequestParam注解,对单个参数的POST请求数据进行绑定的注解,所以自定义注解@RequestPostSingleParam实现POST请求的单个注解绑定。
一、创建注解
/**
* post请求单参数绑定
*
* @Author zhaoxz
* @Data 2023/3/21 16:13
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestPostSingleParam {
@AliasFor("name")
String value() default "";
/**
* The name of the request parameter to bind to.
*
* @since 4.2
*/
@AliasFor("value")
String name() default "";
/**
* Whether the parameter is required.
* <p>Defaults to {@code true}, leading to an exception being thrown
* if the parameter is missing in the request. Switch this to
* {@code false} if you prefer a {@code null} value if the parameter is
* not present in the request.
* <p>Alternatively, provide a {@link #defaultValue}, which implicitly
* sets this flag to {@code false}.
*/
boolean required() default true;
/**
* The default value to use as a fallback when the request parameter is
* not provided or has an empty value.
* <p>Supplying a default value implicitly sets {@link #required} to
* {@code false}.
*/
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
二、RequestPostSingleParam参数解析器
/**
* RequestPostSingleParam参数解析器
*
* @Author zhaoxz
* @Data 2023/3/21 16:18
*/
public class RequestPostSingleParamMethodArgumentResolver implements HandlerMethodArgumentResolver {
private final Logger log = LoggerFactory.getLogger(RequestPostSingleParamMethodArgumentResolver.class);
private static final String POST = "post";
private static final String APPLICATION_JSON = "application/json";
/**
* 判断是否需要处理该参数
*
* @param parameter
* @return boolean
* @Author zhaoxz
* @Date 2023/3/21 16:20
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 只处理带有@RequestPostSingleParam注解的参数
return parameter.hasParameterAnnotation(RequestPostSingleParam.class);
}
/**
* 参数绑定
*
* @param parameter 要解析的方法参数
* @param mavContainer 当前请求的ModelAndViewContainer
* @param webRequest 当前请求
* @param binderFactory 用于创建WebDataBinder实例的工厂
* @return
* @throws Exception
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
// 请求对象
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
// 请求对象空判
String contentType = Objects.requireNonNull(servletRequest).getContentType();
if (contentType == null || !contentType.contains(APPLICATION_JSON)) {
log.error("请求: {}格式错误, contentType需为 {}", servletRequest.getRequestURL().toString(), APPLICATION_JSON);
throw new RuntimeException("请求contentType错误");
}
if (!POST.equalsIgnoreCase(servletRequest.getMethod())) {
log.error("请求: {}格式错误, 请求方式需为 {}", servletRequest.getRequestURL().toString(), POST);
throw new RuntimeException("请求方式错误");
}
// 创建请求
return this.bindRequestParams(parameter, servletRequest);
}
/**
* 创建请求
*
* @param parameter 请求参数
* @param servletRequest 请求对象
* @return Object
* @Author zhaoxz
* @Date 2023/3/21 16:31
*/
private Object bindRequestParams(MethodParameter parameter, HttpServletRequest servletRequest) {
// 获取RequestPostSingleParam注解信息
RequestPostSingleParam requestPostSingleParam = parameter.getParameterAnnotation(RequestPostSingleParam.class);
// 参数类型
Class<?> parameterType = parameter.getParameterType();
// 获取body数据
String requestBody = this.getRequestBody(servletRequest);
// str -> json
JSONObject paramObj = JSONObject.parseObject(requestBody);
// 请求参数为空判定
if (paramObj == null) {
paramObj = new JSONObject();
}
// 绑定参数名称获取
String parameterName = StringUtils.isBlank(requestPostSingleParam.value()) ? parameter.getParameterName()
: requestPostSingleParam.value();
// 获取参数值
Object value = paramObj.get(parameterName);
// 必填判定
if (requestPostSingleParam.required()) {
if (ObjectUtils.isEmpty(value)) {
log.error("请求: {}, {}参数不可为空", servletRequest.getRequestURL().toString(), parameterName);
throw new CustomException("参数【%s】不能为空", parameterName);
}
}
return ConvertUtils.convert(value, parameterType);
}
/**
* 获取请求body
*
* @param servletRequest 请求对象
* @return String body信息
* @Author zhaoxz
* @Date 2023/3/21 16:33
*/
private String getRequestBody(HttpServletRequest servletRequest) {
StringBuilder stringBuilder = new StringBuilder();
try {
BufferedReader reader = servletRequest.getReader();
char[] buf = new char[1024];
int length;
while ((length = reader.read(buf)) != -1) {
stringBuilder.append(buf, 0, length);
}
} catch (IOException e) {
log.error("请求对象读取异常", e);
throw new RuntimeException("获取请求body数据异常");
}
return stringBuilder.toString();
}
}
三、注册参数解析器
@Configuration
public class SpringMvcConfiguration implements WebMvcConfigurer {
/**
* 注册参数解析器
*
* @param resolvers 解析器列表
* @return void
* @Author zhaoxz
* @Date 2023/3/21 16:51
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new RequestPostSingleParamMethodArgumentResolver());
WebMvcConfigurer.super.addArgumentResolvers(resolvers);
}
四、使用测试
@PostMapping("/test")
public ResponseBO<Void> test(@RequestPostSingleParam("testId") String testId) {
log.info("/api/test, 测试【{}】", contestId);
return new ResponseBO<>(SysErrEnum.SUCCESS);
}