【问题标题】:Spring REST get controller + method from route?Spring REST从路由中获取控制器+方法?
【发布时间】:2019-11-23 00:17:27
【问题描述】:

是否有一种内置的 Spring 方式来告诉我“/customers/a”(/customers/{id})的 RestController + 方法? IE。执行标准的 spring 路由,但知道要调用哪个类/方法?

我正试图在一个中心位置根据路线确定该方法是否具有特定注释。用于集中式请求/响应日志记录。

我可以遍历应用程序以获取休息控制器并自己构建地图,但想知道是否有任何东西已经暴露以知道请求会去哪里?

谢谢。

【问题讨论】:

  • 你想拦截所有端点调用来记录它吗?我做对了吗?
  • @RobertoManfreda 我正在使用 Logbook 进行日志记录...我要解决的真正问题是验证注释发生在 OAuth 之前,因此我从未经授权的请求中获取日志条目...但我需要知道特定方法是否允许匿名。
  • 正如@MarkBramnik 所说,您可以使用 Spring AOP 模块。使用 @RestController 和 allMethods 切入点定义建议。使用 @Before 注释,您可以在调用发生之前代理对端点的调用。我认为这是在方法发生之前做某事的干净方法。您可以在端点中注入 @Autowired HttpServletRequest 并从您的 AOP 建议中执行一些验证逻辑
  • @RobertoManfreda 您可以从切入点准备请求正文,并且不会中断传递给真正的方法?从链接来看,您似乎只是调用了 pointcut.proceed()?请求流在我阅读后不会被释放吗?
  • 是的,你是对的。调用您对代理类说的proceed() 方法将请求转发到原始类!但是您可以,例如:使用您的通知拦截请求,验证请求,如果验证正常,您可以在连接点调用 prooceed() 方法,否则您可以直接从通知返回一个新的 HttpResponse(通常是自定义的 ResponseEntity)并从那里设置错误消息和所有必要的! stackoverflow.com/questions/31075594/…

标签: java spring spring-boot


【解决方案1】:

您可以使用 Spring AOP 模块。而且我认为一种干净的方式(在不中断 RestController 端点流的情况下管理响应)可以如下。

首先添加spring-boot-starter-aop依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

让我们看看代码。

这是我们的ExampleRequest

import lombok.Data;

@Data
public class ExampleRequest {
    String name;
}

我们假设我们想要验证我们的请求 -> 如果 name == "anonymous" 我们想要返回一个 HTTP 状态 BAD_REQUEST。


这是我们的TestController

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {
    @Anonymous
    @GetMapping("")
    public ResponseEntity<String> test(ExampleRequest exampleRequest, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        else return new ResponseEntity<>(HttpStatus.OK);
    }    
}

我们将ExampleRequestBindingResult 作为参数传递,这是我们的请求,我们将在稍后使用。无论如何,如果 bindingResult 有错误,我们的验证逻辑就会被破坏,所以我们需要返回我们想要的错误!

@Anonymous 是我们的自定义注解,我们将使用它来告诉我们的 Advice 哪些方法,或者更好 - 哪些类(Aspect 是一个“代理”的整个类),我们想代理

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Anonymous {

}  

还有我们的TestAspect

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;

@Aspect
@Component
public class TestAspect {

    @Before(value = "@annotation(Anonymous) && args(exampleRequest, bindingResult,..)", argNames = "joinPoint,exampleRequest,bindingResult")
    public Object logExecutionTime(JoinPoint joinPoint, ExampleRequest exampleRequest, BindingResult bindingResult) throws Throwable {
        if (exampleRequest.getName().equals("anonymous")) bindingResult.rejectValue("name", "incorrect");
        return joinPoint;
    }

}   

我们的意思是:之前执行EVERY METHODS用我们的自定义ANONYMOUS注释和这些ARGUMENTS注释> 执行这个逻辑!

显然,您将拥有自定义的验证逻辑,因此请实施它。


正如我在上面的 cmets 中所说,我认为您可以直接从代理类返回错误,但同时我认为这个示例是使用 AOP 的一种更简洁的方式(使用 BindingResult,端点的流受到尊重,这技术不会破坏模型类的其他验证。您可以使用 javax 验证,例如混合建议,一切都会完美运行!),我个人喜欢这个 Spring AOP 模块,但不要滥用它嗯>!

供参考:https://docs.spring.io/spring/docs/2.5.x/reference/aop.html

【讨论】:

    【解决方案2】:

    您可能想尝试另一种方法:

    使用 spring 支持的 Aspect Oriented 编程。创建一个切面,作为所有控制器的拦截器。 Advice 将在实际的 rest 方法之前(或“而不是”,取决于通知的类型)被调用,以便您能够获得访问权限,然后将执行转发给真正的控制器。在方面的代码中,您可以自省要在真实控制器上调用的方法并确定它是否具有注释。

    我找到了一个这样做的示例Here(您必须稍微更改切入点定义以使该切面适用于您选择的其余控制器)。

    【讨论】:

    • 嗯...我现在正在使用带有接收器的日志,但我遇到了一个问题,似乎验证注释在 oauth 发生之前触发了对接收器的调用,所以我想过滤通过检查将要使用的方法是否需要授权来发出匿名请求。不过会看看这个 AOP 的东西。日志库旨在记录请求/响应,因此它已经为此做了一些工作。
    • 我在链接中立即发现的一件事是他们似乎没有办法在 POST 上获取请求正文。请求流是只读的。日志通过其他方式捕获它来解决这个问题。
    • 好吧,我不能对日志发表评论,从未使用过它,但一般要求正文不能被阅读两次。它可以读取一次并提供一个伪造的请求对象,该对象将缓存已读取的主体,但同样只发生一次
    • 是的,我认为这就是日志的作用。虽然它们不是 AOP,但它们是基于过滤器的。我的日志几乎可以工作了。我只是想看看是否有一个神奇的 getMethodInfoFromRoute() 类型方法,所以我可以看看它是否有 PreAuthorize 注释......很容易枚举控制器......必须为我的招摇设置做它,所以我也可以重构该部分来执行此操作并构建需要授权的路由缓存。
    • @SledgeHammer 在某种意义上,过滤器 AOP:它们是应用于每个请求的around 建议。
    猜你喜欢
    • 2014-12-15
    • 2018-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多