Весна завантаження Додавання Http Захоплення перехоплювачів


107

Який правильний спосіб додати перехоплювачі HttpRequest у додаток до весняного завантаження? Що я хочу зробити - це запити журналу та відповіді на кожен http-запит.

Документація весняного завантаження зовсім не висвітлює цю тему. ( http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ )

Я знайшов кілька зразків веб-сторінок про те, як зробити те ж саме зі старими версіями весни, але вони працюють з applicationcontext.xml. Будь ласка, допоможіть.


Привіт @ riship89 ... Я HandlerInterceptorуспішно реалізував . Це прекрасно працює. Тільки проблема полягає в тому, що деякі internal HandlerInterceptorкидають виняток, перш ніж це обробляє custom HandlerInterceptor. afterCompletion()Метод , який переопределяется викликається після того , як помилка виникає при внутрішньої реалізації HandlerInterceptor. Чи є у вас рішення для цього?
Четан Осваль

Відповіді:


155

Оскільки ви використовуєте Spring Boot, я припускаю, що ви віддаєте перевагу автоматичній конфігурації Spring, де це можливо. Щоб додати додаткову налаштовану конфігурацію, наприклад ваші перехоплювачі, просто введіть конфігурацію або бобWebMvcConfigurerAdapter .

Ось приклад класу config:

@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() {
        ...
    });
  }
}

ПРИМІТКА не коментуйте це за допомогою @EnableWebMvc, якщо ви хочете зберегти автоматичну конфігурацію Spring Boots для mvc .


Приємно! Виглядає добре! Будь-який приклад того, що входить у register.addInterceptor (...)? Просто цікаво дізнатися зразок "..."
riship89

6
використовувати анотацію @Component на своємуInjectedInceptor
Паоло Біавати

1
@ riship89 Ознайомтесь із цим прикладом: mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example
Влад Мануель

4
Я отримую помилку The type WebMvcConfigurerAdapter is deprecated. Я використовую Spring Web MVC 5.0.6
John Henckel

7
Навесні 5 просто застосуйте WebMvcConfigurer замість розширення WebMvcConfigurerAdapter. Оскільки інтерфейси Java 8 дозволяють реалізувати за замовчуванням реалізацію, більше не потрібно використовувати адаптер (через що він застарів).
edgraaff

88

WebMvcConfigurerAdapterбуде застаріло з весною 5. З його Javadoc :

@deprecked з 5.0 {@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

Оскільки всі відповіді на це використовують вже давно застарілий абстрактний адаптер WebMvcConfigurer замість WebMvcInterface (як уже зазначав @sebdooe), ось робочий мінімальний приклад для програми SpringBoot (2.1.4) із перехоплювачем:

Мінімальний.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, правда?
Стівен

Ні, його (Java 8) інтерфейс з порожніми типовими реалізаціями
Том

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

1
@ xetra11 Я також намагаюся зрозуміти, чи можемо ми реалізувати лише один метод замість всіх інших методів, які не використовуються в даному випадку. Це можливо? Ви це зрозуміли?
користувач09

3
@arjun Інші реалізовані як методи за замовчуванням завдяки Java 8. Це міркування зручно документувати у застарілому класі.
Боб

7

Ви також можете скористатися бібліотекою SpringSandwich з відкритим кодом, яка дозволяє вам безпосередньо коментувати у своїх контролерах Spring Boot, які перехоплювачі застосовувати, так само, як ви коментуєте маршрути своїх URL-адрес.

Таким чином, жодна струна, схильна до друку, що не плаває - метод SpringSandwich та анотації до класу легко переживають рефакторинг і дають зрозуміти, що застосовується куди. (Розкриття: я автор).

http://springsandwich.com/


Це виглядає приголомшливо! Я створив проблему з проханням, щоб SpringSandwich був доступний в центральному центрі Maven, щоб полегшити його використання для проектів, що будуються під CI або які розгортаються через Heroku.
Брендон Дуган

Чудово. Чи доступний він у центральному сховищі Maven? Повторне ведення блогів springsandwich.com на моєму веб-сайті із посиланням на ваш git сховище та довідник
Arpan Das

1
SpringSandwich зараз знаходиться в Центральному районі Мавена
Магнус,

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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.