스프링 부트 애플리케이션에서 HttpRequest 인터셉터를 추가하는 올바른 방법은 무엇입니까? 내가 원하는 것은 모든 http 요청에 대한 요청과 응답을 기록하는 것입니다.
스프링 부트 문서는이 주제를 전혀 다루지 않습니다. ( http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ )
이전 버전의 Spring에서 동일한 작업을 수행하는 방법에 대한 웹 샘플을 찾았지만 applicationcontext.xml에서 작동합니다. 도와주세요.
답변
Spring Boot를 사용하고 있으므로 가능한 경우 Spring의 자동 구성에 의존하는 것을 선호한다고 가정합니다. 인터셉터와 같은 추가 사용자 정의 구성을 추가하려면 구성 또는 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로 주석을 달지 마십시오 .
답변
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());
}
}
답변
스프링 부트 애플리케이션에 인터셉터를 추가하려면 다음을 수행하십시오.
-
인터셉터 클래스 만들기
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 () 메서드에 정의 된 논리를 통과합니다.
답변
이에 대한 모든 응답은 WebMvcInterface (@sebdooe에서 이미 언급했듯이) 대신 오랫동안 사용되지 않는 추상 WebMvcConfigurer 어댑터를 사용하므로 다음은 인터셉터가있는 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");
}
}
답변
URL 경로에 주석을 추가하는 것과 같은 방식으로 인터셉터를 적용 할 Spring Boot 컨트롤러에 직접 주석을 달 수있는 오픈 소스 SpringSandwich 라이브러리 사용을 고려할 수도 있습니다.
이렇게하면 오타가 발생하기 쉬운 문자열이 떠 다니지 않습니다 .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===========================================");
}}
아래는 나머지 템플릿 빈입니다.
@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;
}