add @RequestBodyValue
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 读取 JSON 请求体中的单个字段并绑定到接口参数。
|
||||
*
|
||||
* <p>默认使用方法参数名作为 JSON 字段名,也可以手动指定字段名。
|
||||
*
|
||||
* <pre>
|
||||
* public void run(@RequestBodyValue String data) {
|
||||
* }
|
||||
*
|
||||
* public void run(@RequestBodyValue("value") Long id) {
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2026-04-08 11:00
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequestBodyValue {
|
||||
|
||||
/**
|
||||
* JSON 字段名,为空时使用方法参数名。
|
||||
*
|
||||
* @return JSON 字段名
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* 是否必须存在该字段。
|
||||
*
|
||||
* @return true 表示必须存在
|
||||
*/
|
||||
boolean required() default true;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@link RequestBodyValue} 参数解析器。
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2026-04-08 11:00
|
||||
*/
|
||||
@Component
|
||||
public class RequestBodyValueArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private static final String REQUEST_BODY_JSON_NODE_ATTR = RequestBodyValueArgumentResolver.class.getName() + ".REQUEST_BODY_JSON_NODE";
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* 创建 {@link RequestBodyValue} 参数解析器。
|
||||
*
|
||||
* @param objectMapper Jackson 对象映射器
|
||||
*/
|
||||
public RequestBodyValueArgumentResolver(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(@NonNull MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(RequestBodyValue.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(@NonNull MethodParameter parameter, ModelAndViewContainer mavContainer, @NonNull NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
RequestBodyValue bodyValue = parameter.getParameterAnnotation(RequestBodyValue.class);
|
||||
if (bodyValue == null) {
|
||||
return null;
|
||||
}
|
||||
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
TimiException.required(request, "not found request");
|
||||
assert request != null;
|
||||
|
||||
JsonNode requestBody;
|
||||
{
|
||||
Object cached = request.getAttribute(REQUEST_BODY_JSON_NODE_ATTR);
|
||||
if (cached instanceof JsonNode jsonNode) {
|
||||
return jsonNode;
|
||||
}
|
||||
byte[] bodyBytes = request.getInputStream().readAllBytes();
|
||||
TimiException.requiredTrue(1 < bodyBytes.length, "empty request body");
|
||||
requestBody = objectMapper.readTree(bodyBytes);
|
||||
TimiException.requiredTrue(requestBody != null && requestBody.isObject(), "not object request body");
|
||||
request.setAttribute(REQUEST_BODY_JSON_NODE_ATTR, requestBody);
|
||||
}
|
||||
String fieldName = bodyValue.value();
|
||||
{
|
||||
if (TimiJava.isEmpty(fieldName)) {
|
||||
fieldName = parameter.getParameterName();
|
||||
}
|
||||
TimiException.required(fieldName, "not found @RequestBodyValue parameter name");
|
||||
}
|
||||
JsonNode fieldNode = requestBody.get(fieldName);
|
||||
if (fieldNode == null || fieldNode.isMissingNode() || fieldNode.isNull()) {
|
||||
if (!bodyValue.required()) {
|
||||
return null;
|
||||
}
|
||||
throw new TimiException(TimiCode.ARG_MISS, "not found json field: %s".formatted(fieldName));
|
||||
}
|
||||
return objectMapper.treeToValue(fieldNode, parameter.getParameterType());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user