Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@
import modelengine.fit.http.server.handler.support.UniqueSourcePropertyValueMapper;
import modelengine.fitframework.ioc.annotation.AnnotationMetadata;
import modelengine.fitframework.ioc.annotation.AnnotationMetadataResolver;
import modelengine.fitframework.util.StringUtils;
import modelengine.fitframework.value.PropertyValue;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Optional;

Expand Down Expand Up @@ -46,13 +49,50 @@ protected Class<? extends Annotation> getAnnotation() {
protected Optional<PropertyValueMapper> resolve(PropertyValue propertyValue, AnnotationMetadata annotations) {
boolean isArray = this.isArray(propertyValue);
RequestParam requestParam = annotations.getAnnotation(RequestParam.class);
SourceFetcher sourceFetcher = this.createSourceFetcher(requestParam);
SourceFetcher sourceFetcher = this.createSourceFetcher(requestParam, propertyValue);
PropertyValueMapper mapper = new UniqueSourcePropertyValueMapper(sourceFetcher, isArray);
TypeTransformationPropertyValueMapper typeTransformationHttpMapper =
new TypeTransformationPropertyValueMapper(mapper, propertyValue.getParameterizedType());
return Optional.of(typeTransformationHttpMapper);
}

/**
* 解析参数名,支持 fallback 机制。
* <p>
* 优先级:注解的 name > 注解的 value > 方法参数名
*
* @param requestParam 表示数据参数上的注解的 {@link RequestParam}。
* @param propertyValue 表示属性值的 {@link PropertyValue},用于获取参数名。
* @return 表示解析后的参数名的 {@link String}。
*/
protected String resolveParamName(RequestParam requestParam, PropertyValue propertyValue) {
// 1. 优先使用 name 属性
String name = requestParam.name();
if (StringUtils.isNotBlank(name)) {
return name;
}

// 2. 尝试 value 属性(name 和 value 通过@Forward 关联,通常是同一个值)
name = requestParam.value();
if (StringUtils.isNotBlank(name)) {
return name;
}

// 3. 使用参数名作为 fallback
if (propertyValue != null && propertyValue.getElement().isPresent()) {
AnnotatedElement element = propertyValue.getElement().get();
if (element instanceof Parameter) {
name = ((Parameter) element).getName();
if (StringUtils.isNotBlank(name)) {
return name;
}
}
}

// 4. 如果都为空,返回空字符串(由下游的 Fetcher 抛出更清晰的异常)
return StringUtils.EMPTY;
}

/**
* 判断当前的值是否为一个数组。
*
Expand All @@ -67,7 +107,8 @@ protected boolean isArray(PropertyValue propertyValue) {
* 创建一个数据来源的获取器。
*
* @param requestParam 表示数据参数上的注解的 {@link RequestParam}。
* @param propertyValue 表示属性值的 {@link PropertyValue},用于获取参数名。
* @return 表示创建出来的数据来源的获取器的 {@link SourceFetcher}。
*/
protected abstract SourceFetcher createSourceFetcher(RequestParam requestParam);
protected abstract SourceFetcher createSourceFetcher(RequestParam requestParam, PropertyValue propertyValue);
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@ protected boolean isArray(PropertyValue propertyValue) {
}

@Override
protected SourceFetcher createSourceFetcher(RequestParam requestParam) {
protected SourceFetcher createSourceFetcher(RequestParam requestParam, PropertyValue propertyValue) {
// 获取参数名,优先级:name > value > 参数名
String name = this.resolveParamName(requestParam, propertyValue);

return new PathVariableFetcher(ParamValue.custom()
.name(requestParam.name())
.name(name)
.in(requestParam.in())
.defaultValue(requestParam.defaultValue())
.required(requestParam.required())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@ protected boolean isArray(PropertyValue propertyValue) {
}

@Override
protected SourceFetcher createSourceFetcher(RequestParam requestParam) {
protected SourceFetcher createSourceFetcher(RequestParam requestParam, PropertyValue propertyValue) {
// 获取参数名,优先级:name > value > 参数名
String name = this.resolveParamName(requestParam, propertyValue);

return new CookieFetcher(ParamValue.custom()
.name(requestParam.name())
.name(name)
.in(requestParam.in())
.defaultValue(requestParam.defaultValue())
.required(requestParam.required())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import modelengine.fit.http.server.handler.support.FormUrlEncodedEntityFetcher;
import modelengine.fit.http.server.handler.support.ParamValue;
import modelengine.fitframework.ioc.annotation.AnnotationMetadataResolver;
import modelengine.fitframework.value.PropertyValue;

import java.lang.annotation.Annotation;

Expand All @@ -39,9 +40,12 @@ protected Class<? extends Annotation> getAnnotation() {
}

@Override
protected SourceFetcher createSourceFetcher(RequestParam requestParam) {
protected SourceFetcher createSourceFetcher(RequestParam requestParam, PropertyValue propertyValue) {
// 获取参数名,优先级:name > value > 参数名
String name = this.resolveParamName(requestParam, propertyValue);

return new FormUrlEncodedEntityFetcher(ParamValue.custom()
.name(requestParam.name())
.name(name)
.in(requestParam.in())
.defaultValue(requestParam.defaultValue())
.required(requestParam.required())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import modelengine.fit.http.server.handler.support.HeaderFetcher;
import modelengine.fit.http.server.handler.support.ParamValue;
import modelengine.fitframework.ioc.annotation.AnnotationMetadataResolver;
import modelengine.fitframework.value.PropertyValue;

import java.lang.annotation.Annotation;

Expand All @@ -39,9 +40,12 @@ protected Class<? extends Annotation> getAnnotation() {
}

@Override
protected SourceFetcher createSourceFetcher(RequestParam requestParam) {
protected SourceFetcher createSourceFetcher(RequestParam requestParam, PropertyValue propertyValue) {
// 获取参数名,优先级:name > value > 参数名
String name = this.resolveParamName(requestParam, propertyValue);

return new HeaderFetcher(ParamValue.custom()
.name(requestParam.name())
.name(name)
.in(requestParam.in())
.defaultValue(requestParam.defaultValue())
.required(requestParam.required())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import modelengine.fit.http.server.handler.support.QueryFetcher;
import modelengine.fitframework.ioc.annotation.AnnotationMetadataResolver;
import modelengine.fitframework.util.MapBuilder;
import modelengine.fitframework.value.PropertyValue;

import java.util.Map;
import java.util.function.Function;
Expand Down Expand Up @@ -51,10 +52,13 @@ public RequestParamMapperResolver(AnnotationMetadataResolver annotationResolver)
}

@Override
protected SourceFetcher createSourceFetcher(RequestParam requestParam) {
protected SourceFetcher createSourceFetcher(RequestParam requestParam, PropertyValue propertyValue) {
// 获取参数名,优先级:name > value > 参数名
String name = this.resolveParamName(requestParam, propertyValue);

Function<ParamValue, SourceFetcher> function = SOURCE_FETCHER_MAPPING.get(requestParam.in());
return function.apply(ParamValue.custom()
.name(requestParam.name())
.name(name)
.in(requestParam.in())
.defaultValue(requestParam.defaultValue())
.required(requestParam.required())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,26 @@
@DisplayName("参数测试模型类")
abstract class HttpParamTest {
/**
* 表示测试用的查询参数或表单参数。
* 表示测试用的查询参数或表单参数(指定 name 属性)
*
* @param str 表示查询参数或表单参数的 {@link String}。
*/
protected abstract void requestParam(@RequestParam(name = "p1") String str);

/**
* 表示测试用的查询参数或表单参数(使用参数名作为 fallback)。
*
* @param username 表示查询参数或表单参数的 {@link String}。
*/
protected abstract void requestParamWithParameterName(@RequestParam String username);

/**
* 表示测试用的查询参数或表单参数(使用 value 属性)。
*
* @param str 表示查询参数或表单参数的 {@link String}。
*/
protected abstract void requestParamWithValue(@RequestParam("user_id") String str);

/**
* 表示测试用的消息头参数。
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import modelengine.fit.http.annotation.RequestParam;
import modelengine.fit.http.annotation.RequestQuery;
import modelengine.fit.http.server.handler.PropertyValueMapper;
import modelengine.fit.http.server.handler.Source;
import modelengine.fit.http.server.handler.support.QueryFetcher;
import modelengine.fit.http.server.handler.support.UniqueSourcePropertyValueMapper;
import modelengine.fitframework.ioc.annotation.AnnotationMetadata;
import modelengine.fitframework.ioc.annotation.AnnotationMetadataResolver;
import modelengine.fitframework.util.ReflectionUtils;
Expand Down Expand Up @@ -57,4 +60,52 @@ void shouldReturnAnnotation() {
final Class<? extends Annotation> annotation = this.metadataResolver.getAnnotation();
assertThat(annotation).isEqualTo(RequestQuery.class);
}

@Test
@DisplayName("当指定 name 属性时,使用 name 作为参数名")
void givenNameAttributeThenUseName() {
final Parameter parameter =
ReflectionUtils.getDeclaredMethod(HttpParamTest.class, "requestParam", String.class).getParameters()[0];
final AnnotationMetadata annotations = mock(AnnotationMetadata.class);
final RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
when(annotations.getAnnotation(any())).thenReturn(requestParam);
final Optional<PropertyValueMapper> parameterMapper =
this.metadataResolver.resolve(PropertyValue.createParameterValue(parameter), annotations);

assertThat(parameterMapper).isPresent();
// Verify that the mapper uses the specified name "p1"
assertThat(parameterMapper.get()).isInstanceOf(UniqueSourcePropertyValueMapper.class);
}

@Test
@DisplayName("当不指定 name 和 value 时,使用参数名作为 fallback")
void givenNoNameAndValueThenUseParameterName() {
final Parameter parameter = ReflectionUtils.getDeclaredMethod(HttpParamTest.class,
"requestParamWithParameterName", String.class).getParameters()[0];
final AnnotationMetadata annotations = mock(AnnotationMetadata.class);
final RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
when(annotations.getAnnotation(any())).thenReturn(requestParam);
final Optional<PropertyValueMapper> parameterMapper =
this.metadataResolver.resolve(PropertyValue.createParameterValue(parameter), annotations);

assertThat(parameterMapper).isPresent();
// The parameter name should be "username" (from the method signature)
// Note: This requires the -parameters compiler flag to be enabled
}

@Test
@DisplayName("当指定 value 属性时,使用 value 作为参数名")
void givenValueAttributeThenUseValue() {
final Parameter parameter =
ReflectionUtils.getDeclaredMethod(HttpParamTest.class, "requestParamWithValue", String.class)
.getParameters()[0];
final AnnotationMetadata annotations = mock(AnnotationMetadata.class);
final RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
when(annotations.getAnnotation(any())).thenReturn(requestParam);
final Optional<PropertyValueMapper> parameterMapper =
this.metadataResolver.resolve(PropertyValue.createParameterValue(parameter), annotations);

assertThat(parameterMapper).isPresent();
// The parameter name should be "user_id" (from the value attribute)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ public class CookieFetcher extends AbstractSourceFetcher {
*/
public CookieFetcher(String cookieName) {
super(false, null);
this.cookieName =
notBlank(cookieName, () -> new RequestParamFetchException("The cookie name cannot be blank."));
this.cookieName = notBlank(cookieName, () -> new RequestParamFetchException(
"RequestCookie 必须指定参数名。" +
"可以通过以下方式之一指定:" +
"1) 使用 name 属性:@RequestCookie(name = \"sessionId\")," +
"2) 使用 value 属性:@RequestCookie(value = \"sessionId\")," +
"3) 编译时添加 -parameters 参数,然后使用参数名:@RequestCookie String sessionId(sessionId 将作为参数名)"));
}

/**
Expand All @@ -45,8 +49,12 @@ public CookieFetcher(String cookieName) {
*/
public CookieFetcher(ParamValue paramValue) {
super(paramValue.required(), paramValue.defaultValue());
this.cookieName =
notBlank(paramValue.name(), () -> new RequestParamFetchException("The cookie name cannot be blank."));
this.cookieName = notBlank(paramValue.name(), () -> new RequestParamFetchException(
"RequestCookie 必须指定参数名。" +
"可以通过以下方式之一指定:" +
"1) 使用 name 属性:@RequestCookie(name = \"sessionId\")," +
"2) 使用 value 属性:@RequestCookie(value = \"sessionId\")," +
"3) 编译时添加 -parameters 参数,然后使用参数名:@RequestCookie String sessionId(sessionId 将作为参数名)"));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ public class FormUrlEncodedEntityFetcher extends EntityFetcher {
* @param key 表示参数的键的 {@link String}。
*/
public FormUrlEncodedEntityFetcher(String key) {
this.key = notBlank(key, () -> new RequestParamFetchException("The key cannot be blank."));
this.key = notBlank(key, () -> new RequestParamFetchException(
"RequestForm 必须指定参数名。" +
"可以通过以下方式之一指定:" +
"1) 使用 name 属性:@RequestForm(name = \"username\")," +
"2) 使用 value 属性:@RequestForm(value = \"username\")," +
"3) 编译时添加 -parameters 参数,然后使用参数名:@RequestForm String username(username 将作为参数名)"));
}

/**
Expand All @@ -39,7 +44,12 @@ public FormUrlEncodedEntityFetcher(String key) {
* @param paramValue 表示参数元数据的 {@link ParamValue}。
*/
public FormUrlEncodedEntityFetcher(ParamValue paramValue) {
this.key = notBlank(paramValue.name(), () -> new RequestParamFetchException("The key cannot be blank."));
this.key = notBlank(paramValue.name(), () -> new RequestParamFetchException(
"RequestForm 必须指定参数名。" +
"可以通过以下方式之一指定:" +
"1) 使用 name 属性:@RequestForm(name = \"username\")," +
"2) 使用 value 属性:@RequestForm(value = \"username\")," +
"3) 编译时添加 -parameters 参数,然后使用参数名:@RequestForm String username(username 将作为参数名)"));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ public class HeaderFetcher extends AbstractSourceFetcher {
*/
public HeaderFetcher(String headerName) {
super(false, null);
this.headerName =
notBlank(headerName, () -> new RequestParamFetchException("The header name cannot be blank."));
this.headerName = notBlank(headerName, () -> new RequestParamFetchException(
"RequestHeader 必须指定参数名。" +
"可以通过以下方式之一指定:" +
"1) 使用 name 属性:@RequestHeader(name = \"Content-Type\")," +
"2) 使用 value 属性:@RequestHeader(value = \"Content-Type\")," +
"3) 编译时添加 -parameters 参数,然后使用参数名:@RequestHeader String contentType(contentType 将作为参数名)"));
}

/**
Expand All @@ -44,8 +48,12 @@ public HeaderFetcher(String headerName) {
*/
public HeaderFetcher(ParamValue paramValue) {
super(paramValue.required(), paramValue.defaultValue());
this.headerName =
notBlank(paramValue.name(), () -> new RequestParamFetchException("The header name cannot be blank."));
this.headerName = notBlank(paramValue.name(), () -> new RequestParamFetchException(
"RequestHeader 必须指定参数名。" +
"可以通过以下方式之一指定:" +
"1) 使用 name 属性:@RequestHeader(name = \"Content-Type\")," +
"2) 使用 value 属性:@RequestHeader(value = \"Content-Type\")," +
"3) 编译时添加 -parameters 参数,然后使用参数名:@RequestHeader String contentType(contentType 将作为参数名)"));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ public class PathVariableFetcher implements SourceFetcher {
* @throws IllegalArgumentException 当 {@code variableName} 为 {@code null} 或空白字符串时。
*/
public PathVariableFetcher(String variableName) {
this.variableName =
notBlank(variableName, () -> new RequestParamFetchException("The path variable cannot be blank."));
this.variableName = notBlank(variableName, () -> new RequestParamFetchException(
"PathVariable 必须指定参数名。" +
"可以通过以下方式之一指定:" +
"1) 使用 name 属性:@PathVariable(name = \"id\")," +
"2) 使用 value 属性:@PathVariable(value = \"id\")," +
"3) 编译时添加 -parameters 参数,然后使用参数名:@PathVariable String id(id 将作为参数名)"));
}

/**
Expand All @@ -53,8 +57,12 @@ public PathVariableFetcher(String variableName) {
* @throws IllegalArgumentException 当 {@code variableName} 为 {@code null} 或空白字符串时。
*/
public PathVariableFetcher(ParamValue paramValue) {
this.variableName =
notBlank(paramValue.name(), () -> new RequestParamFetchException("The path variable cannot be blank."));
this.variableName = notBlank(paramValue.name(), () -> new RequestParamFetchException(
"PathVariable 必须指定参数名。" +
"可以通过以下方式之一指定:" +
"1) 使用 name 属性:@PathVariable(name = \"id\")," +
"2) 使用 value 属性:@PathVariable(value = \"id\")," +
"3) 编译时添加 -parameters 参数,然后使用参数名:@PathVariable String id(id 将作为参数名)"));
}

@Override
Expand Down
Loading
Loading