为什么要用 HTTPS(施工中)

一次 HTTP 加解密尝试

工作要处理外部系统对接,虽然网络为政务内网,几乎不可能出现中间人攻击或网络嗅探。但传输内容为敏感信息,仍然要进行加密,最后选择使用 SM4 对整个请求体信息进行加密,并与对接系统约定固定密钥。

确定了一个简单的方案(不建议使用,后面会提到,这个方式局限性很强,唯一优点是实现简单),通过配置高优先级的 Servlet Filter 对请求进行预处理,在请求 Servlet 后读取请求体(此处通过调用 Request.getReader 方法),并对请求体进行解密。创建自定义 RequestWrapper 继承 HttpServletRequestWrapper,并将解密后的自定义 RequestWrapper 中,重写 getReader 和 getInputStream 方法。

这样请求在进入 DispatcherServlet 之前就会完成解密。通过同样方式创建 ResponseWrapper 继承 HttpServletResponseWrapper,在请求返回前由 Servlet Filter 实现对返回响应的整体加密。

该方案支持常规的 POST 请求,如 Content-Type 为 application/json 的请求。

事实上,DispatcherServlet 通过调用 RequestMappingHandlerAdapter 选择合适的 Controller 方法并调用。但在调用之前,还需要完成两步,第一步是解析请求体内容并转换为 Java 对象,第二步是根据 Controller 方法入参,匹配对应的 Java 对象再传入 Controller 方法进行调用。

5.0 版本源码

下面是 RequestMappingHandlerAdapter 的 handle 方法:

// 5.0
@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
	HandlerMethod handlerMethod = (HandlerMethod) handler;
	Assert.state(this.methodResolver != null && this.modelInitializer != null, "Not initialized");

	InitBinderBindingContext bindingContext = new InitBinderBindingContext(
			getWebBindingInitializer(), this.methodResolver.getInitBinderMethods(handlerMethod));
        // 设置并获取 Resolver
	InvocableHandlerMethod invocableMethod = this.methodResolver.getRequestMappingMethod(handlerMethod);

	Function<Throwable, Mono<HandlerResult>> exceptionHandler =
			ex -> handleException(ex, handlerMethod, bindingContext, exchange);

	return this.modelInitializer
			.initModel(handlerMethod, bindingContext, exchange)
                        // 发起调用
			.then(Mono.defer(() -> invocableMethod.invoke(exchange, bindingContext)))
			.doOnNext(result -> result.setExceptionHandler(exceptionHandler))
			.doOnNext(result -> bindingContext.saveModel())
			.onErrorResume(exceptionHandler);
}

在 invocableMethod#invoke 的内部实现中,通过 getMethodArgumentValues 方法,调用 Resolvers 解析控制器方法参数。这里的 Resolvers 实现了 HandlerMethodArgumentResolverComposite 接口,也就是多个 HandlerMethodArgumentResolver 的复合实现,可以灵活地组合多个参数解析器,以支持更多的参数类型和注解。

而具体的处理逻辑应该看 HandlerMethodArgumentResolver 接口实现类,比如说 @RequestBody 注解的参数通过 RequestBodyMethodArgumentResolver 进行解析。查看 RequestBodyMethodArgumentResolver#resolveArgument 方法可以找到具体实现逻辑。HandlerMethodArgumentResolver 也不仅仅包括解析控制器的方法入参,也能够根据 @PathVariable@RequestParam@RequestHeader 等注解解析 HTTP 请求的其他信息。

// 5.0
private Mono<Object[]> getMethodArgumentValues(
        ServerWebExchange exchange, BindingContext bindingContext, Object... providedArgs) {

    MethodParameter[] parameters = getMethodParameters();
    if (ObjectUtils.isEmpty(parameters)) {
        return EMPTY_ARGS;
    }

    List<Mono<Object>> argMonos = new ArrayList<>(parameters.length);
    for (MethodParameter parameter : parameters) {
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        Object providedArg = findProvidedArgument(parameter, providedArgs);
        if (providedArg != null) {
            argMonos.add(Mono.just(providedArg));
            continue;
        }
        if (!this.resolvers.supportsParameter(parameter)) {
            return Mono.error(new IllegalStateException(
                    formatArgumentError(parameter, "No suitable resolver")));
        }
        try {
            // Resolver 处理参数
            argMonos.add(this.resolvers.resolveArgument(parameter, bindingContext, exchange)
                    .defaultIfEmpty(NO_ARG_VALUE)
                    .doOnError(ex -> logArgumentErrorIfNecessary(exchange, parameter, ex)));
        }
        catch (Exception ex) {
            logArgumentErrorIfNecessary(exchange, parameter, ex);
            argMonos.add(Mono.error(ex));
        }
    }
    return Mono.zip(argMonos, values ->
            Stream.of(values).map(value -> value != NO_ARG_VALUE ? value : null).toArray());
}

invoke 发放中的入参 providedArgs 默认不传递,若能找到 parameter 匹配的 Resolver,会调用 resolveArgument 方法。假设此处调用了 RequestBodyMethodArgumentResolver 的方法,则可以通过 readBody 方法获取到入参对象。Spring WebFlux 使用 HttpMessageReader 和 HttpMessageWriter 接口来转换 HTTP 请求和响应,因此在 readBody 方法中,可以看到从 reader 获取对象的逻辑:

// 5.0
for (HttpMessageReader<?> reader : getMessageReaders()) {
    if (reader.canRead(elementType, mediaType)) {
        Map<String, Object> readHints = Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix());
        if (adapter != null && adapter.isMultiValue()) {
            if (logger.isDebugEnabled()) {
                logger.debug(exchange.getLogPrefix() + "0..N [" + elementType + "]");
            }
            // 使用 HttpMessageReader 读取请求
            Flux<?> flux = reader.read(actualType, elementType, request, response, readHints);
            flux = flux.onErrorResume(ex -> Flux.error(handleReadError(bodyParam, ex)));
            if (isBodyRequired) {
                flux = flux.switchIfEmpty(Flux.error(() -> handleMissingBody(bodyParam)));
            }
            if (hints != null) {
                flux = flux.doOnNext(target ->
                        validate(target, hints, bodyParam, bindingContext, exchange));
            }
            return Mono.just(adapter.fromPublisher(flux));
        }
        else {
            // Single-value (with or without reactive type wrapper)
            if (logger.isDebugEnabled()) {
                logger.debug(exchange.getLogPrefix() + "0..1 [" + elementType + "]");
            }
            Mono<?> mono = reader.readMono(actualType, elementType, request, response, readHints);
            mono = mono.onErrorResume(ex -> Mono.error(handleReadError(bodyParam, ex)));
            if (isBodyRequired) {
                mono = mono.switchIfEmpty(Mono.error(() -> handleMissingBody(bodyParam)));
            }
            if (hints != null) {
                mono = mono.doOnNext(target ->
                        validate(target, hints, bodyParam, bindingContext, exchange));
            }
            return (adapter != null ? Mono.just(adapter.fromPublisher(mono)) : Mono.from(mono));
        }
    }
}

看到这里会有一个疑惑,就是为什么代码中是 HttpMessageReader 而不是 HttpMessageConverter。HttpMessageReader 是由 5.0 版本引入的,支持响应式编程的接口,支持读取 ReactiveHttpInputMessage 并编码为对象流。

3.0 版本源码

3.0 版本的代码,逻辑则更加清晰:

下面是通过 DispatcherServlet 进入到 RequestMappingHandlerAdapter 的 handleInternal 方法。

// RequestMappingHandlerAdapter
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
                                      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    checkRequest(request);

    // Execute invokeHandlerMethod in synchronized block if required.
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No HttpSession available -> no mutex necessary
            // 根据请求的内容,解析出方法需要的参数,并准备好这些参数的值
            // 通过Java的反射机制,调用目标处理器方法
            // 处理器方法执行完毕后,invokeHandlerMethod 会获取返回值,并根据返回值的类型做出相应的处理
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    }
    else {
        // No synchronization on session demanded at all...
        // 同上
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        }
        else {
            prepareResponse(response);
        }
    }

    return mav;
}

这里进入 invokeHandlerMethod,该方法会通过传入的 handlerMethod 获取到 invocableMethod,并调用 invocableMethod 的 invokeAndHandle 方法:

// RequestMappingHandlerAdapter
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                           HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    // 使用HTTP请求和响应对象创建 ServletWebRequest 对象,它封装了请求和响应的相关信息
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        // 根据处理器方法,获取数据绑定工厂和模型工厂,用于处理请求参数的绑定和模型数据的准备
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        // 根据处理器方法创建 ServletInvocableHandlerMethod 对象
        // 它是一个可调用的处理器方法,封装了目标处理器方法的信息
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);

        // 如果已配置了参数解析器和返回值处理器,则将它们设置到 ServletInvocableHandlerMethod 对象中
        if (this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        // 创建 ModelAndViewContainer 对象,用于存储处理结果和视图信息
        // 并且从请求中获取输入的 FlashMap 并添加到 ModelAndViewContainer 中
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        // 为异步处理准备 AsyncWebRequest 对象
        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        // 获取当前请求的 WebAsyncManager 对象,用于处理异步请求
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.setTaskExecutor(this.taskExecutor);
        asyncManager.setAsyncWebRequest(asyncWebRequest);
        asyncManager.registerCallableInterceptors(this.callableInterceptors);
        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
        // 如果存在并发结果(通过异步方式处理的结果),则将其处理,并更新 ServletInvocableHandlerMethod 对象以包含这些结果
        if (asyncManager.hasConcurrentResult()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            LogFormatUtils.traceDebug(logger, traceOn -> {
                String formatted = LogFormatUtils.formatValue(result, !traceOn);
                return "Resume with async result [" + formatted + "]";
            });
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }
        // 调用 ServletInvocableHandlerMethod 对象的 invokeAndHandle 方法,执行目标处理器方法并处理结果
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        // 检查异步处理是否已经启动,如果是,则返回 null
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }
        // 从 ModelAndViewContainer 和 ModelFactory 中获取数据,构建并返回最终的 ModelAndView 对象
        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        // 标记请求完成
        webRequest.requestCompleted();
    }
}

invokeAndHandle 方法内通过 invokeForRequest 方法获取到返回值,该方法内部则又看到了熟悉的 getMethodArgumentValues

// ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                            Object... providedArgs) throws Exception {

    // 调用 invokeForRequest 方法执行目标处理器方法
    // 传入 ServletWebRequest 对象、ModelAndViewContainer 对象和其他提供的参数
    // 方法的返回值保存在 returnValue 变量中
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    // 调用 setResponseStatus 方法来设置响应的状态码
    setResponseStatus(webRequest);
    // 返回值为 null
    if (returnValue == null) {
        // 如果
        // 1. 请求未被修改(通过判断是否为未修改请求)
        // 2. 或者已经设置了响应状态码
        // 3. 或者 ModelAndViewContainer 标记为已处理请求
        // 则禁用内容缓存并将 ModelAndViewContainer 标记为已处理,并直接返回
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            disableContentCachingIfNecessary(webRequest);
            mavContainer.setRequestHandled(true);
            return;
        }
    // 返回值不为 null,但是具有响应状态码的原因(通过 getResponseStatusReason 方法判断)
    } else if (StringUtils.hasText(getResponseStatusReason())) {
        // 则将 ModelAndViewContainer 标记为已处理请求,并直接返回
        mavContainer.setRequestHandled(true);
        return;
    }
    // 如果没有满足上述条件,将 ModelAndViewContainer 标记为未处理请求,并确保 returnValueHandlers 不为 null
    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        // 尝试使用 returnValueHandlers 来处理返回值
        // returnValueHandlers 是一组用于处理控制器方法返回值的策略
        this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    } catch (Exception ex) {
        if (logger.isTraceEnabled()) {
            logger.trace(formatErrorForReturnValue(returnValue), ex);
        }
        throw ex;
    }
}

------

// InvocableHandlerMethod
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                               Object... providedArgs) throws Exception {

    // 调用 getMethodArgumentValues 方法获取目标处理器方法的参数值
    // 这个方法会解析请求,准备方法调用所需的参数
    // 入参包括 NativeWebRequest 对象、ModelAndViewContainer 对象(可能为 null),以及其他提供的参数
    // 这些参数将在方法内部被解析和处理,最终得到一个参数值的数组
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    return doInvoke(args);
}

------

// InvocableHandlerMethod
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                           Object... providedArgs) throws Exception {
    // 调用 getMethodParameters 方法获取目标处理器方法的参数信息
    MethodParameter[] parameters = getMethodParameters();
    // 如果没有参数,则直接返回一个空数组
    if (ObjectUtils.isEmpty(parameters)) {
        return EMPTY_ARGS;
    }
    // 遍历方法的参数列表
    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        // 对每个方法参数对象调用 initParameterNameDiscovery 方法初始化参数名发现器,以便后续解析参数名
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        // 首先尝试在提供的参数数组中查找是否有提供的参数值。如果有则直接将提供的参数值赋给对应的参数变量
        args[i] = findProvidedArgument(parameter, providedArgs);
        if (args[i] != null) {
            continue;
        }
        // 如果没有提供的参数值,则判断当前方法参数是否由当前的参数解析器(resolvers)支持
        if (!this.resolvers.supportsParameter(parameter)) {
            throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
        try {
            // 如果支持当前参数,则调用参数解析器的 resolveArgument 方法来解析参数值,并将解析结果赋给对应的参数变量
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
        }
        catch (Exception ex) {
            // Leave stack trace for later, exception may actually be resolved and handled...
            if (logger.isDebugEnabled()) {
                String exMsg = ex.getMessage();
                if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                    logger.debug(formatArgumentError(parameter, exMsg));
                }
            }
            throw ex;
        }
    }
    return args;
}

------

// HandlerMethodArgumentResolverComposite
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                              NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    // 获取参数处理器
    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
        throw new IllegalArgumentException("Unsupported parameter type [" +
                parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
    }
    // 参数处理
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

最终的链路还是一致的,到了 resolver.resolveArgument 方法。我们还是以 RequestResponseBodyMethodProcessor 为例:

// RequestResponseBodyMethodProcessor
// 该方法用于解析控制器方法的参数,特别是用于处理使用 @RequestBody 和 @ResponseBody 注解的方法参数
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                              NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    parameter = parameter.nestedIfOptional();
    // 调用 readWithMessageConverters 方法
    // 使用 HttpMessageConverters 去解析请求体,并获取参数值。这一步骤将请求体中的数据转换成方法参数所需的类型
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    // 获取参数的名称,用于后续数据绑定
    String name = Conventions.getVariableNameForParameter(parameter);
    // 如果存在 binderFactory(数据绑定工厂)
    if (binderFactory != null) {
        // 调用 createBinder 方法创建数据绑定器,并对参数值进行数据绑定
        // 数据绑定器会将请求参数的值绑定到方法参数上,并进行验证
        WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
        // 如果参数值不为空,则进行数据验证,如果验证失败且需要抛出绑定异常,则抛出 MethodArgumentNotValidException 异常
        if (arg != null) {
            validateIfApplicable(binder, parameter);
            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
            }
        }
        // 如果存在 mavContainer(ModelAndViewContainer),则将数据绑定的结果添加到 mavContainer 中
        if (mavContainer != null) {
            mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
        }
    }
    // 根据需要对参数值进行适应。这一步骤可能涉及对参数值的进一步处理或转换
    return adaptArgumentIfNecessary(arg, parameter);
}

------

// RequestResponseBodyMethodProcessor
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
                                               Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    // 从 NativeWebRequest 中获取 HttpServletRequest 对象。这个对象是原生的HTTP请求对象
    HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
    Assert.state(servletRequest != null, "No HttpServletRequest");
    // 创建 ServletServerHttpRequest 对象。这个对象封装了HTTP请求的信息,方便后续对请求体的处理
    ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
    // 调用另一个重载的 readWithMessageConverters 方法,传入 ServletServerHttpRequest 对象、方法参数对象和参数类型
    // 这个方法是实际执行消息转换的关键步骤,它会尝试使用消息转换器将请求体转换为方法参数所需的类型,并返回转换后的参数值
    Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
    if (arg == null && checkRequired(parameter)) {
        throw new HttpMessageNotReadableException("Required request body is missing: " +
                parameter.getExecutable().toGenericString(), inputMessage);
    }
    return arg;
}

最后进入 readWithMessageConverters 方法,这里也是完成与 HttpMessageConverter 关联的位置:

// AbstractMessageConverterMethodArgumentResolver
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
                                               Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

    MediaType contentType;
    boolean noContentType = false;
    try {
        // 尝试从输入消息的头部获取内容类型(Content-Type)
        contentType = inputMessage.getHeaders().getContentType();
    }
    catch (InvalidMediaTypeException ex) {
        // 如果获取失败,则抛出 HttpMediaTypeNotSupportedException 异常
        throw new HttpMediaTypeNotSupportedException(ex.getMessage());
    }
    // 如果没有指定内容类型,则将内容类型设为默认的 MediaType.APPLICATION_OCTET_STREAM。
    if (contentType == null) {
        noContentType = true;
        contentType = MediaType.APPLICATION_OCTET_STREAM;
    }
    // 根据方法参数的类型和目标类型,确定转换后的目标类型
    Class<?> contextClass = parameter.getContainingClass();
    // 如果目标类型不是一个 Class 类型,则从方法参数中获取相应的 Class 类型
    Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
    if (targetClass == null) {
        ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
        targetClass = (Class<T>) resolvableType.resolve();
    }
    // 如果输入消息是一个 HttpRequest 类型的消息,则获取其中的 HTTP 请求方法
    // 这用于后续判断是否支持当前 HTTP 请求方法
    HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
    Object body = NO_VALUE;

    AbstractMessageConverterMethodArgumentResolver.EmptyBodyCheckingHttpInputMessage message;
    try {
        // 初始化一些变量,包括 body、message 等
        message = new AbstractMessageConverterMethodArgumentResolver.EmptyBodyCheckingHttpInputMessage(inputMessage);
        // 对注册的消息转换器列表进行遍历,尝试使用每个消息转换器来读取输入消息并进行转换
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            // 对每个消息转换器,判断它是否能够处理当前请求
            Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
            GenericHttpMessageConverter<?> genericConverter =
                    (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
            // 如果能够处理,则根据消息转换器的类型进行相应的读取和转换操作
            // genericConverter 不为 null,表示当前转换器是一个通用的消息转换器,可以处理任意类型的目标对象
            if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                // genericConverter 为 null,表示当前转换器不是通用的,此时判断是否支持指定目标类型和内容类型
                (targetClass != null && converter.canRead(targetClass, contentType))) {
                // 输入消息中存在消息体
                if (message.hasBody()) {
                    // 调用 beforeBodyRead 方法,对消息体进行预处理
                    HttpInputMessage msgToUse =
                            getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                    // 使用消息转换器将输入消息的消息体转换为目标类型的对象
                    body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                            ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                    // 对转换后的结果进行后处理
                    body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                }
                else {
                    // 如果输入消息中没有消息体,则调用相应的回调方法处理空消息体
                    body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                }
                break;
            }
        }
    }
    catch (IOException ex) {
        throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
    }
    // 如果没有成功地读取到消息体
    if (body == NO_VALUE) {
        // 并且请求方法不在支持的方法列表中,或者消息体为空且没有指定内容类型
        if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
                (noContentType && !message.hasBody())) {
            // 则返回 null
            return null;
        }
        // 否则,抛出 HttpMediaTypeNotSupportedException 异常
        throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
    }

    MediaType selectedContentType = contentType;
    Object theBody = body;
    LogFormatUtils.traceDebug(logger, traceOn -> {
        String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
        return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
    });
    // 如果成功读取到了消息体并进行了转换,则返回转换后的结果
    // 在返回之前,记录日志以便跟踪转换的结果和过程
    return body;
}

为什么用 Filter 不好?

创建自定义 RequestWrapper 继承 HttpServletRequestWrapper 前,就需要读取请求体再进行解密操作。

Servlet 容器(比如 Tomcat、Jetty 等)在接收到 HTTP 请求后,会解析请求头和消息体,并提供一种方式让 Servlet 来读取这些数据。Servlet 通常通过 HttpServletRequest 对象提供的 getReader() 方法(用于读取字符数据)或 getInputStream() 方法(用于读取字节数据)来读取请求体中的数据。

然而,由于 HTTP 协议的设计,一旦读取了请求体中的数据,它们就会从输入流中被读取并且被消费掉了。换句话说,一旦调用了 getReader() 或 getInputStream() 方法,Servlet 容器会从请求中读取消息体,并将它们提供给Servlet。一旦 Servlet 读取完消息体或者关闭了输入流,消息体中的数据就不再可用了。因此,再次调用 getReader() 或getInputStream() 方法将不会返回有效的数据。可以在 Catalina 的 Request 方法中看到,usingReader 字段用于控制 getReader() 或 getInputSream() 方法被调用两次。

// Request
@Override
public BufferedReader getReader() throws IOException {

    if (usingInputStream) {
        throw new IllegalStateException(sm.getString("coyoteRequest.getReader.ise"));
    }

    // InputBuffer has no easily accessible reference chain to the Context
    // to check for a default request character encoding at the Context.
    // Therefore, if a Context default should be used, it is set explicitly
    // here. Need to do this before setting usingReader.
    if (coyoteRequest.getCharacterEncoding() == null) {
        // Nothing currently set explicitly.
        // Check the content
        Context context = getContext();
        if (context != null) {
            String enc = context.getRequestCharacterEncoding();
            if (enc != null) {
                // Explicitly set the context default so it is visible to
                // InputBuffer when creating the Reader.
                setCharacterEncoding(enc);
            }
        }
    }

    usingReader = true;

    inputBuffer.checkConverter();
    if (reader == null) {
        reader = new CoyoteReader(inputBuffer);
    }
    return reader;
}

实际在使用 Filter 处理 multipart/form-data 的请求时,对文件流的支持并不好。Controller 参数处理类型为 MultipartFile 时,会使用 StandardServletMultipartResolver

@Override
public void cleanupMultipart(MultipartHttpServletRequest request) {
    if (!(request instanceof AbstractMultipartHttpServletRequest) ||
            ((AbstractMultipartHttpServletRequest) request).isResolved()) {
        // To be on the safe side: explicitly delete the parts,
        // but only actual file parts (for Resin compatibility)
        try {
            // 调用了 HttpServletRequest.getParts() 方法
            for (Part part : request.getParts()) {
                if (request.getFile(part.getName()) != null) {
                    part.delete();
                }
            }
        }
        catch (Throwable ex) {
            LogFactory.getLog(getClass()).warn("Failed to perform cleanup of multipart items", ex);
        }
    }
}

StandardServletMultipartResolver 通过调用 getParts() 方法,该方法也会调用 Request.getReader() 导致抛出异常。诚然自定义 RequestWrapper 可以将请求体内容缓存,并重写 getReader() 方法,但是 StandardServletMultipartResolver 中还是会调用 Request.getReader() 方法。