SpringBoot自定义注解实现Post接收单个参数值
前言
由于 Spring 没有提供类似于 @RequestParam
注解,对单个参数的 Post 请求数据进行绑定的注解,所以,如果遇到某些情况会格外麻烦,如下:
使用 Map 接收 Post 请求参数:
@PostMapping("/delete") public AjaxResult delete(@RequestBody Map<String,String> params){ Long id = params.get("id"); if (id == null) { throw AppException("参数错误"); } service.deleteById(id); return AjaxResult.success(); }
使用 String 接收 Post 请求参数
@PostMapping("/delete") public AjaxResult delete(@RequestBody String params){ JSONObject paramsJSONObject = JSONObject.parseObject(params); Long id = paramsJSONObject.getLong("id"); if (id == null) { throw AppException("参数错误"); } service.deleteById(id); return AjaxResult.success(); }
从 Request 中获取参数
@PostMapping("/delete") public AjaxResult delete(HttpServletRequest request) { String body = getRequestBody(request); JSONObject paramsJSONObject = JSONObject.parseObjec(body); Long id = paramsJSONObject.getLong("id"); if (id == null) { throw AppException("参数错误"); } service.deleteById(id); return AjaxResult.success(); } /** * 从 request 中获取 body */ 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 AppException(SystemError.PARSE_PARAMS_FAIL); } return stringBuilder.toString(); }
从 对象中获取
@PostMapping("/delete") public ApiResponse delete(@RequestBody IdBean idBean) { if (idBean == null || idBean.getId() == null) { throw AppException("参数错误"); } service.deleteById(id); return ApiResponse.createBySuccess(); }
@Data public class IdBean { private Long id; }
可以很明显的看到,虽然功能满足了,但是,不觉得有点怪吗,所以我们可以模仿 @RequestParam
自定义一个注解 @CustomParam
,实现和 @RequestParam
同样的功能,只不过 @RequestParam
注解是从请求路径上获取参数,而我们自定义的 @CustomParam
注解则是从 request body 中获取参数
实现
1.定义注解
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomParam {
@AliasFor("name")
String value() default "";
//要绑定到的请求参数的名称
@AliasFor("value")
String name() default "";
//是否是必须的
boolean required() default true;
//当请求参数值未提供或为空时用作回退的默认值。提供默认值会隐式设置
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
2.编写参数解析器
@Slf4j
public class CustomMethodArgumentResolver implements HandlerMethodArgumentResolver {
private static final String POST = "post";
private static final String APPLICATION_JSON = "application/json";
/**
* 判断是否需要处理该参数
*
* @param parameter the method parameter to check
* @return {@code true} if this resolver supports the supplied parameter;
* {@code false} otherwise
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 只处理带有@CustomParam注解的参数
return parameter.hasParameterAnnotation(CustomParam.class);
}
@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需为{}", APPLICATION_JSON);
throw new RuntimeException("解析参数异常,contentType需为application/json");
}
if (!POST.equalsIgnoreCase(servletRequest.getMethod())) {
log.error("解析参数异常,请求类型必须为post");
throw new RuntimeException("解析参数异常,请求类型必须为post");
}
return bindRequestParams(parameter, servletRequest);
}
private Object bindRequestParams(MethodParameter parameter, HttpServletRequest servletRequest) {
CustomParam customParam = parameter.getParameterAnnotation(CustomParam.class);
Class<?> parameterType = parameter.getParameterType();
String requestBody = getRequestBody(servletRequest);
Map paramObj = JSONObject.parseObject(requestBody, Map.class);
String name = Strings.isBlank(customParam.value()) ? parameter.getParameterName() : customParam.value();
Object value = paramObj.get(name);
if (parameterType.equals(String.class)) {
if (StringUtils.isBlank((String) value)) {
log.error("参数解析异常,String类型参数不能为空");
throw new RuntimeException("参数解析异常,String类型参数不能为空");
}
}
if (customParam.required()) {
if (value == null) {
log.error("参数解析异常,require=true,值不能为空");
throw new RuntimeException("参数解析异常,require=true,值不能为空");
}
} else {
if (customParam.defaultValue().equals(ValueConstants.DEFAULT_NONE)) {
log.error("参数解析异常,require=false,必须指定默认值");
throw new RuntimeException("参数解析异常,require=false,必须指定默认值");
}
if (value == null) {
value = customParam.defaultValue();
}
}
return ConvertUtils.convert(value, parameterType);
}
/**
* 获取请求body
*
* @param servletRequest request
* @return 请求body
*/
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("读取流异常");
}
return stringBuilder.toString();
}
}
3.注册参数解析器
@Configuration
public class CustomParamResolverConfigurer implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new CustomMethodArgumentResolver());
WebMvcConfigurer.super.addArgumentResolvers(resolvers);
}
}
4.完成
这样我们的功能就实现了,使用方法:
@PostMapping("/delete")
public AjaxResult delete(@CustomParam Long id) {
service.deleteById(id);
return AjaxResult.success();
}
写在最后,可能需要引入的包:
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils-core -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils-core</artifactId>
<version>1.8.3</version>
</dependency>
参考
文章1:https://juejin.cn/post/6844904122433404942
文章2:https://blog.csdn.net/qq_38225558/article/details/112572928