【问题标题】:RestTemplate InterceptorRestTemplate 拦截器
【发布时间】:2019-12-22 07:36:55
【问题描述】:

我目前正在尝试合并HandlerInterceptorAdapter,但它没有被注册,并且将其与其他答案进行比较很困难,因为每个人都在使用不同的东西。而且我知道 WebMvcConfigureAdapter 已被弃用,某些版本控制超出了我对项目范围的控制范围,请参阅下面的使用规范。

有人可以提供一些关于将拦截器与 RestTemplate(不是 ClientHttpRequestInterceptor)结合的指导。

主要:

@SpringBootApplication
@EnableRetry
public class Application extends SpringBootServletInitializer {

  public static void main(String[] args) {

   ApplicationContext ctx = SpringApplication.run(Application.class, args);

  }


  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder applicationBuilder) {

    return applicationBuilder.sources(Application.class);

  }

  @Bean
  private RestTemplate restTemplate(){
    Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("redacted", 8080));

    SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
    simpleClientHttpRequestFactory.setProxy(proxy);
    simpleClientHttpRequestFactory.setOutputStreaming(false);

    RestTemplate template = new RestTemplate();
    template.setErrorHandler(new MyResponseErrorHandler());

    return template;
  }
}

拦截器:com.example.foo.config.request.interceptor

@Component
public class MyInterceptor extends HandlerInterceptorAdapter {

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("INTERCEPTED");
    return super.preHandle(request, response, handler);
  }
}

InterceptorConfig : com.example.foo.config.request.interceptor

@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter  {

  @Bean
  MyInterceptor myInterceptor() {
    return new MyInterceptor();
  }

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    super.addInterceptors(registry);
    System.out.println("Adding interceptor");
    registry.addInterceptor(myInterceptor());
  }

}

“添加拦截器”确实被记录下来,所以我知道正在扫描配置。我只是无法记录任何拦截器逻辑。

使用:

  • Spring Boot v1.5.15
  • 春季版:4.3.18.RELEASE

【问题讨论】:

  • 我认为问题与您启动RestTemplate 有关。请使用自动连线@Autowired private RestTemplate restTemplate;。甚至拦截器也应该是Autowired。我建议以这个项目为例github.com/Asatnin/SpringMicroservices/blob/…
  • 谢谢@TarunLalwani 我在问这个问题之前尝试了 AutoWiring,但这没关系,我也在我的服务中 AutoWire 其余模板。您的示例看起来与我的设置非常相似,但是您的拦截器使用了其余交换方法,所以这可能是关键。我会尝试更新你

标签: java spring interceptor resttemplate


【解决方案1】:

HandlerInterceptorAdapter是供服务器端(即RestController)在服务器处理HTTP请求时拦截一些重要事件的,与使用什么HTTP客户端(如RestTemplate)无关。

如果您想使用RestTemplate 作为HTTP 客户端,并且想在请求发出之前拦截请求并在收到响应之后拦截,则必须使用ClientHttpRequestInterceptor

我正在尝试以更灵活的方式拦截请求和响应 比 ClientHttpRequestInterceptor。

根据您上面的评论,您无法处理的实际用例是什么?我认为ClientHttpRequestInterceptor 已经足够灵活,可以实现任何复杂的逻辑来拦截请求和响应。由于您的问题没有提供任何有关您需要如何拦截的信息,我只能举一个一般性的例子来说明ClientHttpRequestInterceptor 可以提供什么。

将 RestTemplate 配置为使用拦截器:

RestTemplate rt = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors= new ArrayList<ClientHttpRequestInterceptor>();
inteceptors.add(new MyClientHttpRequestInterceptor());

ClientHttpRequestInterceptor 看起来像:

public class MyClientHttpRequestInterceptor implements ClientHttpRequestInterceptor{

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
            throws IOException {

        //The HTTP request and its body are intercepted here which you can log them or modify them. e.g.
        System.out.println("Log the HTTP request header: " + request.getHeaders());

        //Modify the HTTP request header....
        request.getHeaders().add("foo", "fooValue");

        //Throw exception if you do not want to send the HTTP request

        //If it is at the end of the interceptor chain , call execution.execute() to confirm sending the HTTP request will return the response in ClientHttpResponse
        //Otherwise, it will pass the request to the next interceptor in the chain to process
        ClientHttpResponse response= execution.execute(request, body);

        //The HTTP response is intercepted here which you can log them or modify them.e.g.
        System.out.println("Log the HTTP response header: " + response.getHeaders());

        //Modify the HTTP response header
        response.getHeaders().add("bar", "barValue");

        return response;
    }
}

请注意,您还可以配置ClientHttpRequestInterceptor 链,允许将一些复杂的请求和响应拦截逻辑拆分为许多小块和可重复使用的ClientHttpRequestInterceptor。它采用Chain of responsibility 设计模式设计,其API 体验与Servlet 中的Filter#doFilter() 非常相似。

【讨论】:

  • 我完全和你在一起。也许他可以/可以/应该创建一个实现两个接口和/或继承自具有相似性的抽象超类的类?
  • 我不太明白的是,如果OP只是想在使用HTTP客户端时拦截请求/响应,这有什么关系,为什么需要关心服务器如何拦截请求/响应?
  • 我已阅读所有答案和 cmets,但我也不太明白。一方面他害怕被弃用,另一方面他宁愿使用RestTemplate而不是WebClient。 API 规定了它的要求,我们必须满足这些要求。我的建议是/是我最好的猜测,他可能想要达到的目标。
  • 我不怕弃用,正如我所提到的,版本控制超出了我的控制范围,ClientHttpRequestInterceptor 不能满足我的需求,为了争论,我们甚至可以随便说一下。为什么它不相关是无关紧要的,并且通过提出“太宽泛”或“不确定你在问什么”的问题绝对会违反 SO 礼节。我已将询问范围缩小到非常简单的内容,即使用非 ClientHttpRequestInterceptor 的 RestTemplate 拦截请求/响应。归根结底,原因并不重要,问题是如何
  • @soulshined ,明白。那么你的最终解决方案是如何变成的呢?我重新阅读了接受答案,但仍然找不到有关如何在不使用 ClientHttpRequestInterceptor 的情况下使用 RestTemplate 进行操作的任何信息。我在这里错过了什么吗??? 您能否分享您的最终解决方案,以便这里的社区可以学习一种可能比RestTemplate 提供的现有ClientHttpRequestInterceptor 方式更好的拦截方式? ?‍♂️?‍♂️?‍♂️谢谢。
【解决方案2】:

正如@WonChulHeo 所说,您不能将HandlerInterceptorAdapterRestTemplate 一起使用。只有ClientHttpRequestInterceptor。目前尚不清楚您为什么确切需要HandlerInterceptorAdapter - 我们只能看到您正在尝试记录请求拦截的事实。 ClientHttpRequestInterceptor 绝对能够做到这一点,甚至更多——请查看下面的工作示例。

附:您的代码中有错误 - 您不能使用 private 访问 @Bean 方法 - 请检查您的 private RestTemplate restTemplate() {...

@Slf4j
@RestController
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class)
                .bannerMode(Banner.Mode.OFF)
                .run(args);
    }

    @GetMapping("/users/{id}")
    public User get(@PathVariable int id) {
        log.info("[i] Controller: received request GET /users/{}", id);
        return new User(id, "John Smith");
    }

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder templateBuilder) {
        ClientHttpRequestFactory requestFactory = new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());
        return templateBuilder
                .interceptors((request, bytes, execution) -> {
                    URI uri = request.getURI();
                    HttpMethod method = request.getMethod();

                    log.info("[i] Interceptor: requested {} {}", method, uri);
                    log.info("[i] Interceptor: request headers {}", request.getHeaders());

                    ClientHttpRequest delegate = requestFactory.createRequest(uri, method);
                    request.getHeaders().forEach((header, values) -> delegate.getHeaders().put(header, values));

                    ClientHttpResponse response = delegate.execute();
                    log.info("[i] Interceptor: response status: {}", response.getStatusCode().name());
                    log.info("[i] Interceptor: response headers: {}", response.getHeaders());
                    String body = StreamUtils.copyToString(response.getBody(), Charset.defaultCharset());
                    log.info("[i] Interceptor: response body: '{}'", body);

                    return response;
                })
                .rootUri("http://localhost:8080")
                .build();
    }

    @Bean
    ApplicationRunner run(RestTemplate restTemplate) {
        return args -> {
            ResponseEntity<User> response = restTemplate.getForEntity("/users/{id}", User.class, 1);
            if (response.getStatusCode().is2xxSuccessful()) {
                log.info("[i] User: {}", response.getBody());
            } else {
                log.error("[!] Error: {}", response.getStatusCode());
            }
        };
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
}

【讨论】:

  • 感谢您的意见。如前所述,我并没有尝试使用 ClientHttpRequestInterceptor,原因是这是一个独特的边缘案例,对未来的读者没有帮助,并且可能会征求不适用的答案。
【解决方案3】:

RestTemplate 期望 ClientHttpRequestInterceptor

setInterceptors(List<ClientHttpRequestInterceptor> interceptors)

设置此访问器应使用的请求拦截器。

您可以使用Servlet Filter 来“拦截”请求/响应,

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

使用 servlet 过滤器来实现这一点。这里根本不涉及 Spring

但是您必须将 RestTemplate 更改为使用其他框架作为 jersey

Jersey 提供了一个非常方便的实现,例如称为 LoggingFilter 的过滤器,它可以帮助记录各种传入和传出流量。

【讨论】:

  • 谢谢。我会尝试一个过滤器,让你更新!这可能是一个明显的疏忽!感谢您简洁明了。有时这不是关于 为什么 OP 想要这样做,而只是关于 如何 完成它
  • Servlet 过滤器只能拦截进入应用程序的请求。我猜OP想要的是拦截通过restTemplate从应用程序中发出的请求。我是正确的@soulshined 吗?
  • @user7294900 只告诉拦截进入控制器的请求,对吗?还是我错过了什么?它会拦截通过 resttemplate 或任何其他 http 客户端从应用程序发出的请求吗?
  • @MadhuBhat 我用链接更新了我的答案你必须将 RestTemplate 更改为使用其他框架作为球衣
【解决方案4】:

HandlerInterceptorAdapter 是适用于@Controller@RestController 的实现。不是RestTemplete 的实现。

要将其应用于RestTemplete,您需要使用ClientHttpRequestInterceptor

例如

@Component
public class CustomInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        // ... 
    }
}
@Configuation
public class RestTempleteConfig {

    // ...
    @Autowired
    private CustomInterceptor customInterceptor;

    @Bean
    public RestTemplate restTemplate(){
        RestTemplate template = new RestTemplate();
        List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
        template.add(customInterceptor);
        return template;
    }
}

【讨论】:

  • OP 明确提到这不是一个理想的解决方案。当然,我使用的是服务和 RestController 的混合体。不过感谢您的反馈!
猜你喜欢
  • 1970-01-01
  • 2015-11-08
  • 2021-05-20
  • 1970-01-01
  • 2018-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多