原因
由于前端框架原因,后端post请求一致用applition/json 请求。对于后端而言实体类需要加上@RequestBody注解,但是有时候修改接口等只需要一两个参数,如果将其分装为实体,费时费力。因此可以自己写一个参数处理器。
解决
可以自己写一个参数处理器。实现HandlerMethodArgumentResolver接口,再将其配置到WebMvcConfigurer类中的参数处理集合当中。
具体代码
- 注解:
/**
* tip:只支持application/json格式下 的基本数据类型包装类,和Sring
*
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonParam {
/**
* 值
*/
String value() default "";
/**
* 是否必须
*/
boolean require() default false;
}
- 参数处理器:
/**
* 参数转换(POST,默认前端以JSON形式提交)
*/
public class Json2ParamsHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
private static final Logger LOG = LoggerUtils.getLogger(Json2ParamsHandlerMethodArgumentResolver.class);
/**
* key
*/
private static final String JSON_BODY_KEY = "JSON_BODY_KEY";
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(JsonParam.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
// 获取jsonNode
JsonNode jsonNode = getJsonBody(webRequest);
JsonParam requestSingleParam = parameter.getParameterAnnotation(JsonParam.class);
// 名
String value = determineParamName(parameter, requestSingleParam);
// 结果
JsonNode paramValue = jsonNode.get(value);
// 是否必须
boolean require = requestSingleParam.require();
if (paramValue == null && require) {
throw new ServerException("parameter[" + value + "]不能为空。");
}
if (paramValue == null) {
return null;
}
Class<?> parameterType = parameter.getParameterType();
Constructor<?> constructor = parameterType.getConstructor(String.class);
Object param = null;
try {
param = constructor.newInstance(paramValue.asText());
} catch (Exception e) {
LOG.error("bind method parameters error", e);
throw new ServerException("parameter[" + value + "] format error for input value[" + paramValue.toString() + "]");
}
return param;
}
private String determineParamName(MethodParameter parameter, JsonParam requestSingleParam) {
String value = requestSingleParam.value();
if (BaseVaildedUtils.isEmpty(value)) {
value = parameter.getParameterName();
}
return value;
}
private JsonNode getJsonBody(NativeWebRequest webRequest) {
// 有就直接获取
String jsonBody = (String) webRequest.getAttribute(JSON_BODY_KEY, NativeWebRequest.SCOPE_REQUEST);
// 没有就从请求中读取
if (jsonBody == null) {
try {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
jsonBody = IOUtils.toString(servletRequest.getReader());
webRequest.setAttribute(JSON_BODY_KEY, jsonBody, NativeWebRequest.SCOPE_REQUEST);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return JsonUtil.parseObject(jsonBody);
}
}
- 配置类:
@Configuration
public class BaseMvcConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new Json2ParamsHandlerMethodArgumentResolver());
}
}
结语:
这样就可以使用@JsonParam 来标记基础类型包装类(包括String)的参数,来实现Application/json的方式提交json字符串无法接受的问题了。
tip: 上述代码其实可以和@RequestBody的解析器合并成同一个,这样使用体验更佳。因为时间关系,暂时没做研究。