Spring Boot HTTP 요청 인터셉터 추가


107

스프링 부트 애플리케이션에서 HttpRequest 인터셉터를 추가하는 올바른 방법은 무엇입니까? 내가 원하는 것은 모든 http 요청에 대한 요청과 응답을 기록하는 것입니다.

스프링 부트 문서는이 주제를 전혀 다루지 않습니다. ( http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ )

이전 버전의 Spring에서 동일한 작업을 수행하는 방법에 대한 웹 샘플을 찾았지만 applicationcontext.xml에서 작동합니다. 도와주세요.


안녕하세요 @ riship89 ... HandlerInterceptor성공적으로 구현 했습니다. 잘 작동합니다. 문제는 일부 internal HandlerInterceptorcustom HandlerInterceptor. afterCompletion()재정 의 된 메서드는 HandlerInterceptor의 내부 구현에 의해 오류가 발생한 후에 호출됩니다. 이에 대한 해결책이 있습니까?
Chetan Oswal

답변:


155

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로 주석을 달지 마십시오 .


좋은! 좋아 보인다! registry.addInterceptor (...) 내부에 들어가는 예가 있습니까? (A)의 샘플 "..."알고 그냥 궁금해서
riship89

6
yourInjectedInceptor에 사용 @Component 주석
파올로 Biavati


4
오류가 발생 The type WebMvcConfigurerAdapter is deprecated합니다. 저는 Spring Web MVC 5.0.6을 사용하고 있습니다
John Henckel

7
Spring 5에서는 WebMvcConfigurerAdapter를 확장하는 대신 WebMvcConfigurer를 구현하십시오. Java 8 인터페이스는 기본 구현을 허용하므로 더 이상 어댑터를 사용할 필요가 없습니다 (이는 더 이상 사용되지 않는 이유입니다).
edgraaff

88

WebMvcConfigurerAdapterSpring 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());
    }
}

10
귀하의 답변은 구현이 누락 되었기 때문에 불완전합니다MyCustomInterceptor
peterchaula

33

스프링 부트 애플리케이션에 인터셉터를 추가하려면 다음을 수행하십시오.

  1. 인터셉터 클래스 만들기

    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;
        }
    }
  2. 구성 클래스 정의

    @Configuration
    public class MyConfig extends WebMvcConfigurerAdapter{
        @Override
        public void addInterceptors(InterceptorRegistry registry){
            registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/**");
        }
    }
  3. 그게 다야. 이제 모든 요청이 MyCustomInterceptor의 preHandle () 메서드에 정의 된 논리를 통과합니다.


1
이 방법을 따라 몇 가지 일반적인 유효성 검사를 수행하기 위해 내 응용 프로그램에 오는 가입 요청을 가로 챌 수 있습니다. 하지만 문제는getReader() has already been called for this request 오류가 발생 입니다. 실제 요청의 사본을 사용하지 않고 이것을 극복하는 더 간단한 방법이 있습니까?
vigamage

사전 처리기가 호출되면 요청 본문을 사용할 수 없지만 매개 변수 만 사용할 수 있습니다. 요청 본문에 대한 유효성 검사를 수행하려면 Aspect J를 사용하고 생성하는 것이 좋습니다.Advice
Hima

12

이에 대한 모든 응답은 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

하지만 이렇게하려면 WebMvcConfigurer의 모든 메서드를 구현해야합니다. 그렇죠?
Steven

아니요, 기본 구현이 비어있는 (Java 8) 인터페이스
Tom

9

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");
    }
}

4
컴파일 문제없이 인터페이스에서 하나의 메서드 만 재정의 할 수있는 방법은 무엇입니까?
xetra11 '1711.29

1
@ xetra11 나는 또한이 경우에 사용되지 않는 다른 모든 방법 대신 하나의 방법 만 구현할 수 있는지 확인하려고합니다. 가능할까요? 알아 냈어?
user09

3
@arjun 나머지는 Java 8 덕분에 기본 메소드 로 구현됩니다. 이 이유는 사용되지 않는 클래스에 편리하게 문서화 되어 있습니다.

7

URL 경로에 주석을 추가하는 것과 같은 방식으로 인터셉터를 적용 할 Spring Boot 컨트롤러에 직접 주석을 달 수있는 오픈 소스 SpringSandwich 라이브러리 사용을 고려할 수도 있습니다.

이렇게하면 오타가 발생하기 쉬운 문자열이 떠 다니지 않습니다 .SpringSandwich의 메서드와 클래스 주석은 쉽게 리팩토링에서 살아남고 어디에 적용되고 있는지 명확하게합니다. (공개 : 저자입니다).

http://springsandwich.com/


멋지네요! 저는 SpringSandwich를 maven central에서 사용할 수 있도록 요청하는 문제를 만들어서 CI 하에서 빌드 중이거나 Heroku를 통해 배포하는 프로젝트를 더 쉽게 사용할 수 있도록했습니다.
Brendon Dugan

큰. Maven 중앙 저장소에서 사용할 수 있습니까? 다시 -git 저장소 및 참조 참조와 함께 내 웹 사이트에서 springsandwich.com 블로깅
Arpan Das

1
SpringSandwich는 현재 Maven Central에 있습니다
Magnus

1

다음은 나가기 전에 각 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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.