问题
I'm currently trying to incorporate a HandlerInterceptorAdapter
but it's not getting registered and comparing it to other answers is tough because everyone is using something different. And I'm aware WebMvcConfigureAdapter is deprecated, some versioning is beyond my control for the scope of the project, see usage specs below.
Can someone please provide some guidance on incorporating interceptors with a RestTemplate (that's not ClientHttpRequestInterceptor).
Main:
@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;
}
}
Interceptor : 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());
}
}
"Adding interceptor" does get logged so I know the configs are being scanned. I just can't get any interceptor logic to log.
Using:
- Spring Boot v1.5.15
- Spring Version: 4.3.18.RELEASE
回答1:
RestTemplate expects ClientHttpRequestInterceptor
setInterceptors(List<ClientHttpRequestInterceptor> interceptors)
Set the request interceptors that this accessor should use.
You can use Servlet Filter to "intercept" requests/response,
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response;
implement this with a servlet filter. No Spring involved here at all
But you will have to change RestTemplate to using other framework as jersey
Jersey gives a very handy implementation of such as filter called LoggingFilter which can help in logging all kinds of incoming and outgoing traffic.
回答2:
HandlerInterceptorAdapter
is an implementation that applies to @Controller
or @RestController
. Not an implementation for RestTemplete
.
To apply it to RestTemplete
, you need to use ClientHttpRequestInterceptor
.
ex.
@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;
}
}
回答3:
As @WonChulHeo noted you can't use HandlerInterceptorAdapter
with RestTemplate
. Only ClientHttpRequestInterceptor
. It's not clear why do you need exactly HandlerInterceptorAdapter
- we can only see that you are trying to log the fact of the request interception. And ClientHttpRequestInterceptor
is absolutely able to do the same and even more - check my working example bellow.
P.S. There is an error in your code - you can't use private
access for @Bean
methods - check your private RestTemplate restTemplate() {
please...
@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;
}
回答4:
The HandlerInterceptorAdapter
is for the server side (i.e. RestController) to intercept some important events when server processes a HTTP request , it is nothing to do with what HTTP client (e.g RestTemplate
) is used.
If you want to use RestTemplate
as a HTTP client and want to intercept the request just before it sent out and the response just after it received , you must use ClientHttpRequestInterceptor
.
I’m trying to intercept requests and responses in a more flexible way than ClientHttpRequestInterceptor.
From your comment above , what is your actual use-cases that it cannot handle ? I think ClientHttpRequestInterceptor
is already quite flexible enough to implement any complex logic to intercept request and response. As your question does not provide any information about how you need to intercept , I can only give a general example to show what the ClientHttpRequestInterceptor
can offer .
To configure the RestTemplate to use an interceptor :
RestTemplate rt = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors= new ArrayList<ClientHttpRequestInterceptor>();
inteceptors.add(new MyClientHttpRequestInterceptor());
And the ClientHttpRequestInterceptor looks like:
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;
}
}
Please note that you can also configure a chain of ClientHttpRequestInterceptor
which allows to split some complex request and response intercept logic into many smalls and reusable ClientHttpRequestInterceptor
. It is designed with the Chain of responsibility design pattern which its API experience is very similar to what Filter#doFilter()
in Servlet
.
来源:https://stackoverflow.com/questions/57516825/resttemplate-interceptor