All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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