在Spring Boot应用程序中添加HttpRequest拦截器的正确方法是什么?我想做的是记录每个HTTP请求的请求和响应。
Spring Boot文档完全没有涵盖该主题。(http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/)
我发现了一些有关如何对较早版本的spring执行相同操作的Web示例,但这些示例与applicationcontext.xml一起使用。请帮忙。
在Spring Boot应用程序中添加HttpRequest拦截器的正确方法是什么?我想做的是记录每个HTTP请求的请求和响应。
Spring Boot文档完全没有涵盖该主题。(http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/)
我发现了一些有关如何对较早版本的spring执行相同操作的Web示例,但这些示例与applicationcontext.xml一起使用。请帮忙。
Answers:
由于您使用的是Spring Boot,因此我假设您希望在可能的情况下依靠Spring的自动配置。要添加其他自定义配置(例如拦截器),只需提供一个配置或的bean WebMvcConfigurerAdapter
。
这是一个配置类的例子:
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Autowired
HandlerInterceptor yourInjectedInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(...)
...
registry.addInterceptor(getYourInterceptor());
registry.addInterceptor(yourInjectedInterceptor);
// next two should be avoid -- tightly coupled and not very testable
registry.addInterceptor(new YourInterceptor());
registry.addInterceptor(new HandlerInterceptor() {
...
});
}
}
注意,如果要保留mvc的Spring Boots自动配置,请不要使用@EnableWebMvc进行注释。
The type WebMvcConfigurerAdapter is deprecated
。我正在使用Spring Web MVC 5.0.6
WebMvcConfigurerAdapter
将在Spring 5中弃用。从其Javadoc中:
从5.0开始,@ deprecated {@link WebMvcConfigurer}具有默认方法(由Java 8基准实现),可以直接实现而无需此适配器
如上所述,您应该做的是实现WebMvcConfigurer
和覆盖addInterceptors
方法。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyCustomInterceptor());
}
}
MyCustomInterceptor
要将拦截器添加到Spring Boot应用程序,请执行以下操作
创建一个拦截器类
public class MyCustomInterceptor implements HandlerInterceptor{
//unimplemented methods comes here. Define the following method so that it
//will handle the request before it is passed to the controller.
@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response){
//your custom logic here.
return true;
}
}
定义配置类
@Configuration
public class MyConfig extends WebMvcConfigurerAdapter{
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/**");
}
}
而已。现在,您所有的请求都将通过MyCustomInterceptor的preHandle()方法定义的逻辑。
getReader() has already been called for this request
错误。有没有更简单的方法可以解决此问题而不使用实际请求的副本?
Advice
由于对此的所有响应都使用了现已弃用的抽象WebMvcConfigurer适配器而不是WebMvcInterface适配器(已由@sebdooe指出),因此,这是一个带有Interceptor的SpringBoot(2.1.4)应用程序的最小工作示例:
Minimal.java:
@SpringBootApplication
public class Minimal
{
public static void main(String[] args)
{
SpringApplication.run(Minimal.class, args);
}
}
MinimalController.java:
@RestController
@RequestMapping("/")
public class Controller
{
@GetMapping("/")
@ResponseBody
public ResponseEntity<String> getMinimal()
{
System.out.println("MINIMAL: GETMINIMAL()");
return new ResponseEntity<String>("returnstring", HttpStatus.OK);
}
}
Config.java:
@Configuration
public class Config implements WebMvcConfigurer
{
//@Autowired
//MinimalInterceptor minimalInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(new MinimalInterceptor());
}
}
MinimalInterceptor.java:
public class MinimalInterceptor extends HandlerInterceptorAdapter
{
@Override
public boolean preHandle(HttpServletRequest requestServlet, HttpServletResponse responseServlet, Object handler) throws Exception
{
System.out.println("MINIMAL: INTERCEPTOR PREHANDLE CALLED");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
{
System.out.println("MINIMAL: INTERCEPTOR POSTHANDLE CALLED");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception
{
System.out.println("MINIMAL: INTERCEPTOR AFTERCOMPLETION CALLED");
}
}
如广告所示
输出将为您提供以下信息:
> Task :Minimal.main()
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.4.RELEASE)
2019-04-29 11:53:47.560 INFO 4593 --- [ main] io.minimal.Minimal : Starting Minimal on y with PID 4593 (/x/y/z/spring-minimal/build/classes/java/main started by x in /x/y/z/spring-minimal)
2019-04-29 11:53:47.563 INFO 4593 --- [ main] io.minimal.Minimal : No active profile set, falling back to default profiles: default
2019-04-29 11:53:48.745 INFO 4593 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2019-04-29 11:53:48.780 INFO 4593 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2019-04-29 11:53:48.781 INFO 4593 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-04-29 11:53:48.892 INFO 4593 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-04-29 11:53:48.893 INFO 4593 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1269 ms
2019-04-29 11:53:49.130 INFO 4593 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-04-29 11:53:49.375 INFO 4593 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-04-29 11:53:49.380 INFO 4593 --- [ main] io.minimal.Minimal : Started Minimal in 2.525 seconds (JVM running for 2.9)
2019-04-29 11:54:01.267 INFO 4593 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-04-29 11:54:01.267 INFO 4593 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-04-29 11:54:01.286 INFO 4593 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 19 ms
MINIMAL: INTERCEPTOR PREHANDLE CALLED
MINIMAL: GETMINIMAL()
MINIMAL: INTERCEPTOR POSTHANDLE CALLED
MINIMAL: INTERCEPTOR AFTERCOMPLETION CALLED
我已弃用WebMvcConfigurerAdapter的同一问题。搜索示例时,几乎找不到任何已实现的代码。这是一段工作代码。
创建一个扩展HandlerInterceptorAdapter的类
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import me.rajnarayanan.datatest.DataTestApplication;
@Component
public class EmployeeInterceptor extends HandlerInterceptorAdapter {
private static final Logger logger = LoggerFactory.getLogger(DataTestApplication.class);
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String x = request.getMethod();
logger.info(x + "intercepted");
return true;
}
}
然后实施WebMvcConfigurer接口
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import me.rajnarayanan.datatest.interceptor.EmployeeInterceptor;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
EmployeeInterceptor employeeInterceptor ;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(employeeInterceptor).addPathPatterns("/employee");
}
}
您可能还考虑使用开源的SpringSandwich库,该库使您可以在Spring Boot控制器中直接注释要应用的拦截器,与注释URL路由的方式几乎相同。
这样,就不会出现容易打错字串的情况-SpringSandwich的方法和类注释很容易在重构后幸存下来,并清楚说明了将在哪里应用。(披露:我是作者)。
下面是一个我用来拦截每个HTTP请求并返回响应的实现。通过此实现,我还可以在一点上随请求传递任何标头值。
public class HttpInterceptor implements ClientHttpRequestInterceptor {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public ClientHttpResponse intercept(
HttpRequest request, byte[] body,
ClientHttpRequestExecution execution
) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.add("Accept", MediaType.APPLICATION_JSON_UTF8_VALUE);
headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
traceRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
traceResponse(response);
return response;
}
private void traceRequest(HttpRequest request, byte[] body) throws IOException {
logger.info("===========================Request begin======================================");
logger.info("URI : {}", request.getURI());
logger.info("Method : {}", request.getMethod());
logger.info("Headers : {}", request.getHeaders() );
logger.info("Request body: {}", new String(body, StandardCharsets.UTF_8));
logger.info("==========================Request end=========================================");
}
private void traceResponse(ClientHttpResponse response) throws IOException {
logger.info("============================Response begin====================================");
logger.info("Status code : {}", response.getStatusCode());
logger.info("Status text : {}", response.getStatusText());
logger.info("Headers : {}", response.getHeaders());
logger.info("=======================Response end===========================================");
}}
下面是Rest模板Bean
@Bean
public RestTemplate restTemplate(HttpClient httpClient)
{
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate= new RestTemplate(requestFactory);
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors))
{
interceptors = new ArrayList<>();
}
interceptors.add(new HttpInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
HandlerInterceptor
成功实现了。运行正常。唯一的问题是,某些方法internal HandlerInterceptor
在由处理之前引发了异常custom HandlerInterceptor
。在afterCompletion()
HandlerInterceptor的内部实现引发错误之后,将调用被覆盖的方法。您对此有解决方案吗?