请求输入无效时,控制器周围的Spring AOP不起作用 [英] Spring AOP around controllers does not work when request input are invalid

查看:127
本文介绍了请求输入无效时,控制器周围的Spring AOP不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用@Around编写了请求/响应记录器:

I have written a request/response logger using @Around:

@Around(value = "execution(* com.xyz.example.controller.*.*(..))")
public Object logControllers(ProceedingJoinPoint joinPoint) throws Throwable {
    Object response = joinPoint.proceed();

    // Log request and response

    return response;
}

但是,我意识到提供的请求输入(即请求正文)是否无效,例如,如果number是请求正文中的必填字段,并且必须为Integer,但是我输入了String作为其值并发送到端点,Spring将返回400响应而不会遇到此问题.但是,如果我输入了一些合法的输入,则让请求实际上通过端点,那么此方面将完成其工作.那么有什么方法可以使这方面在我上面提到的情况下起作用?

However, I realized if the request input (ie. request body) provided are invalid, for example, if number is a required field in the request body, and it must be an Integer, but I entered a String as its value and send to the endpoint, Spring will return a 400 response without hitting this aspect. But if I entered some legit input, let the request actually goes through the endpoint, then this aspect will do its job. So is there any way to let this aspect works under the situation I mentioned above?

PS:我曾尝试将@ControllerAdvice@ExceptionHandler结合使用,但是如果触发@ControllerAdvice,看起来它也不会通过上述记录器方面.

PS: I have tried using @ControllerAdvice with @ExceptionHandler, but looks like it will also not go through the above logger aspect if @ControllerAdvice is triggered.

推荐答案

首先,让我解释一下为什么您对方面和控制器建议的尝试失败了.

First of all, let me explain why your attempts with aspect and controller advice have failed.

流程是这样的(大大简化了,但还是希望深度"到足以说明这一点):

The flow goes like this (greatly simplified but still hopefully 'deep' enough to explain the point):

  1. 该请求命中了网络中包含的内容(例如,tomcat)
  2. Tomcat将HTTP原始请求文本(有效载荷)转换为表示http请求(javaServlet请求等)的java类
  3. 如果指定了请求,则该请求将通过对原始"请求对象进行操作的javax.servlet.Filter-s传递.
  4. Tomcat查找用于处理请求的servlet.在这种情况下,它是Spring MVC的DispatcherServlet
  5. Tomcat将执行传递给DispatcherServlet.此时,春天就开始显现了.
  6. DispatcherServlet找到要调用的控制器
  7. DispatcherServlet了解"如何将数据从原始请求转换为控制器中指定的方法的参数(应将什么映射到"path",如何转换Body等).例如,如果预期使用JSON,则将使用Jackson进行实际转换.
  8. 控制器被调用

现在,将AOP部分重新分级: AOP建议可以有效地包装您的控制器,从而提供与实际控制器无法区分"的代理.因此,可以在步骤"8"期间调用此代理(对Spring MVC透明)

Now, regrading the AOP part: AOP advice wraps your controller effectively providing a proxy "indistinguishable" from the real controller. So this proxy could be invoked (transparently to Spring MVC) during the step "8"

这是一条快乐之路".但是,如果查询错误,该怎么办?

This is a "happy path". However, what happens if the query is wrong?

  • 如果它不是Http请求,它将在第2步中失败,并且显然不会到达第8步.
  • 如果它是有效的http请求,但未正确映射(例如错误的url映射路径等),DispatcherServlet将找不到相关的控制器来处理它,因此它将在步骤"6"期间失败.
  • 如果JSON有效负载错误,则第7步将失败.

无论如何它都不会到达第8步,这就是为什么您的建议没有被调用的原因

现在有关@ControllerAdvice.它是Spring MVC中的特殊"异常处理机制,有助于正确映射控制器(或控制器调用的类,例如服务,Dao等)内部发生的异常.由于流量甚至还没有到达控制器,因此在这里它几乎是无关紧要的.

Now regarding the @ControllerAdvice. Its a "special" exception handling mechanism in spring MVC that helps to properly map exceptions that happen inside the controller (or the class that controller calls to, like service, Dao, etc.). Since the flow haven't even reached the controller, its pretty much irrelevant here.

现在的术语或解决方案:

Now in terms or resolution:

基本上可以通过编程来实现两个抽象:

There are basically two abstractions that you can try to do it programmatically:

  • javax.servlet的过滤器以Web容器的方式完成
  • HandlerInterceptorAdapter 以Spring MVC的方式实现
  • javax.servlet's Filter to do it in a web container's way
  • HandlerInterceptorAdapter to do it in a spring MVC's way

在两种情况下,您都必须处理原始请求. 这是spring mvc方式的示例:

In both cases you'll have to deal with the raw request. Here is an example of the spring mvc way:

@Component
public class LoggingInterceptor 
  extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(
      HttpServletRequest request, 
      HttpServletResponse response, 
      Object handler) {
        return true; // called before the actual controller is invoked
    }

    @Override
    public void afterCompletion(
      HttpServletRequest request, 
      HttpServletResponse response, 
      Object handler, 
      Exception ex) {
        // called after the controller has processed the request,
        // so you might log the request here
    }
}

要像这样正确注册和映射拦截器,可以使用WebMvcConfigurer.全部查看示例

In order to register and map the interceptor like this correctly you can use WebMvcConfigurer. All-in-all see this example

其他解决方案包括:

  1. Spring Boot Actuator的端点记录了50个最近的请求
  2. Tomcat(例如,如果您使用tomcat)具有特殊的Valve,可以记录请求(访问日志样式).例如,请参见此线程

这篇关于请求输入无效时,控制器周围的Spring AOP不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆