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()); } }