Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
cc.shacocloud.mirage.web.bind.support.AbstractNamedValueMethodArgumentResolver Maven / Gradle / Ivy
package cc.shacocloud.mirage.web.bind.support;
import cc.shacocloud.mirage.web.HandleMethodArgumentResolver;
import cc.shacocloud.mirage.web.HttpRequest;
import cc.shacocloud.mirage.web.bind.ValueConstants;
import cc.shacocloud.mirage.web.bind.WebDataBinder;
import cc.shacocloud.mirage.web.bind.WebDataBinderFactory;
import cc.shacocloud.mirage.web.exception.HttpRequestBindingException;
import cc.shacocloud.mirage.web.exception.MethodArgumentConversionNotSupportedException;
import cc.shacocloud.mirage.web.exception.MethodArgumentTypeMismatchException;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Future;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.core.MethodParameter;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 抽象基类,用于从指定值解析方法参数。请求参数、请求头和路径变量都是命名值的例子。每个都可能有一个名称、一个是否必需的标志和一个默认值。
*
*
* 子类定义如何做以下事情:
*
* 获取方法参数的命名值信息
* 将名称解析为参数值
* 当需要参数值时处理缺少的参数值
* 可选地处理解析值
*
*/
public abstract class AbstractNamedValueMethodArgumentResolver implements HandleMethodArgumentResolver {
private final Map namedValueInfoCache = new ConcurrentHashMap<>(256);
@Override
public Future resolveArgument(HttpRequest request,
MethodParameter parameter,
@Nullable WebDataBinderFactory binderFactory) {
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
String resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException("指定的名称不能解析为null: [" + namedValueInfo.name + "]");
}
return resolveName(resolvedName, nestedParameter, request)
.compose(arg -> {
if (arg == null) {
// 默认值
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
// 缺失值的处理情况
} else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, request);
}
// 空值的处理情况
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
} else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
// 参数转换
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(request, null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
} catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
} catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, request);
return Future.succeededFuture(arg);
});
}
/**
* 获取给定方法参数的命名值。
*/
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
if (namedValueInfo == null) {
namedValueInfo = createNamedValueInfo(parameter);
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
this.namedValueInfoCache.put(parameter, namedValueInfo);
}
return namedValueInfo;
}
/**
* 为给定的方法参数创建{@link NamedValueInfo}对象。
* 实现通常通过{@link MethodParameter#getParameterAnnotation(Class)}检索方法注解。
*
* @param parameter 该方法的参数
* @return 命名值信息
*/
protected abstract NamedValueInfo createNamedValueInfo(MethodParameter parameter);
/**
* 基于给定的NamedValueInfo创建一个新的NamedValueInfo。
*/
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
String name = info.name;
if (info.name.isEmpty()) {
name = parameter.getParameterName();
if (name == null) {
throw new IllegalArgumentException("参数类型名称 [" + parameter.getNestedParameterType().getName() + "] 不可用,并且在类文件中也找不到参数名称信息。");
}
}
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
return new NamedValueInfo(name, info.required, defaultValue);
}
/**
* 解析给定的注解指定值,拓展方法。
*/
@Nullable
protected abstract String resolveStringValue(String value);
/**
* 将给定的参数类型和值名称解析为参数值。
*
* @param name 要解析的值的名称
* @param parameter 要解析为参数值的方法参数 (预先嵌套的情况下{@link java.util.Optional})
* @param request 当前请求
* @return 已解析的参数
*/
protected abstract Future resolveName(String name, MethodParameter parameter, HttpRequest request);
/**
* 当需要指定值时调用, 但是{@link #resolveName} 返回了{@code null},并且没有默认值。在这种情况下,子类通常会抛出异常。
*
* @param name 值的名称
* @param parameter 该方法的参数
* @param request 当前请求
*/
protected void handleMissingValue(String name, MethodParameter parameter, HttpRequest request) throws HttpRequestBindingException {
// 默认状态码
request.response().setStatusCode(HttpResponseStatus.BAD_REQUEST.code());
handleMissingValue(name, parameter);
}
/**
* 当需要指定值时调用, 但是{@link #resolveName} 返回了{@code null},并且没有默认值。在这种情况下,子类通常会抛出异常。
*
* @param name 值的名称
* @param parameter 该方法的参数
*/
protected void handleMissingValue(String name, MethodParameter parameter) throws HttpRequestBindingException {
throw new HttpRequestBindingException("无法匹配参数类型为[" + parameter.getNestedParameterType().getSimpleName()
+ "]参数名称为 '" + name + "' 的值");
}
/**
* {@code null}会导致{@code Boolean}的{@code false}值,或其他原语的异常。
*/
@Nullable
private Object handleNullValue(String name, @Nullable Object value, Class> paramType) {
if (value == null) {
if (Boolean.TYPE.equals(paramType)) {
return Boolean.FALSE;
} else if (paramType.isPrimitive()) {
throw new IllegalStateException("可选的 " + paramType.getSimpleName() + " 参数 '" + name + "' 存在,但由于声明为原始类型,无法转换为空值。考虑将其声明为对应原语类型的对象包装器。");
}
}
return value;
}
/**
* 在解析值后调用。
*
* @param arg 已解析的参数值
* @param name 参数名称
* @param parameter 参数参数类型
* @param request 当前请求
*/
protected void handleResolvedValue(@Nullable Object arg,
String name,
MethodParameter parameter,
HttpRequest request) {
}
/**
* 表示关于命名值的信息,包括名称、是否需要以及默认值。
*/
protected static class NamedValueInfo {
private final String name;
private final boolean required;
@Nullable
private final String defaultValue;
public NamedValueInfo(String name, boolean required, @Nullable String defaultValue) {
this.name = name;
this.required = required;
this.defaultValue = defaultValue;
}
}
}