Пролет - Влезте в входящите заявки

1. Въведение

В този бърз урок ще покажем основите на регистрирането на входящите заявки с помощта на филтъра за регистриране на Spring. Ако тепърва започвате с регистриране, разгледайте тази статия за вписване, както и статията SLF4J.

2. Зависимости на Maven

Зависимостите при регистриране просто ще бъдат същите като тези в уводната статия; нека просто добавим пролетта тук:

 org.springframework spring-core 5.2.2.RELEASE 

Най-новата версия може да бъде намерена тук за spring-core.

3. Основен уеб контролер

Първо, нека дефинираме контролер, който ще се използва в нашия пример:

@RestController public class TaxiFareController { @GetMapping("/taxifare/get/") public RateCard getTaxiFare() { return new RateCard(); } @PostMapping("/taxifare/calculate/") public String calculateTaxiFare( @RequestBody @Valid TaxiRide taxiRide) { // return the calculated fare } }

4. Регистриране на заявки по поръчка

Spring предоставя механизъм за конфигуриране на дефинирани от потребителя прехващачи за извършване на действия преди и след уеб заявки.

Сред прослушващите прехващачи на заявки един от забележителните интерфейси е HandlerInterceptor , който може да се използва за регистриране на входящата заявка чрез прилагане на следните методи:

  1. preHandle () - този метод се изпълнява преди действителния метод за обслужване на контролера
  2. afterCompletion () - този метод се изпълнява, след като контролерът е готов да изпрати отговора

Освен това Spring осигурява изпълнението по подразбиране на интерфейса HandlerInterceptor под формата на клас HandlerInterceptorAdaptor, който може да бъде разширен от потребителя.

Нека създадем наш собствен прехващач - като разширим HandlerInterceptorAdaptor като:

@Component public class TaxiFareRequestInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) { return true; } @Override public void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // } }

И накрая, ще конфигурираме TaxiRideRequestInterceptor вътре в жизнения цикъл на MVC, за да уловим предварителна и последваща обработка на извикванията на методите на контролера, които са съпоставени с път / такси, дефинирани в класа TaxiFareController .

@Configuration public class TaxiFareMVCConfig implements WebMvcConfigurer { @Autowired private TaxiFareRequestInterceptor taxiFareRequestInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(taxiFareRequestInterceptor) .addPathPatterns("/**/taxifare/**/"); } }

В заключение WebMvcConfigurer добавя TaxiFareRequestInterceptor вътре в пролетния жизнен цикъл на MVC, като извиква метода addInterceptors () .

Най-голямото предизвикателство е да се получат копията на полезния товар за заявки и отговори за регистриране и все пак да се остави исканият полезен товар за сървлета да го обработи.

Основният проблем с искането за четене е, че веднага щом входният поток се прочете за първи път, той се маркира като консумиран и не може да се прочете отново.

Приложението ще изведе изключение, след като прочете заявката:

{ "timestamp": 1500645243383, "status": 400, "error": "Bad Request", "exception": "org.springframework.http.converter .HttpMessageNotReadableException", "message": "Could not read document: Stream closed; nested exception is java.io.IOException: Stream closed", "path": "/rest-log/taxifare/calculate/" }

За да преодолеем този проблем , можем да използваме кеширането, за да съхраним потока от заявки и да го използваме за регистриране.

Spring предоставя няколко полезни класа като ContentCachingRequestWrapper и ContentCachingResponseWrapper, които могат да се използват за кеширане на данните на заявката с цел регистриране.

Нека коригираме нашия preHandle () на клас TaxiRideRequestInterceptor, за да кешира обекта на заявката, използвайки класа ContentCachingRequestWrapper .

@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { HttpServletRequest requestCacheWrapperObject = new ContentCachingRequestWrapper(request); requestCacheWrapperObject.getParameterMap(); // Read inputStream from requestCacheWrapperObject and log it return true; }

Както виждаме, кеширахме обекта на заявката, използвайки клас ContentCachingRequestWrapper, който може да се използва за четене на данните за полезния товар за регистриране, без да нарушаваме действителния обект на заявката:

requestCacheWrapperObject.getContentAsByteArray();

Ограничение

  • Класът ContentCachingRequestWrapper поддържа само следното:
Content-Type:application/x-www-form-urlencoded Method-Type:POST
  • Трябва да извикаме следния метод, за да гарантираме, че данните за заявките са кеширани в ContentCachingRequestWrapper, преди да ги използваме:
requestCacheWrapperObject.getParameterMap();

5. Пролетно регистриране на вградени заявки

Spring предоставя вградено решение за регистриране на полезния товар. Можем да използваме готови филтри, като се включим в приложение Spring чрез конфигурация.

AbstractRequestLoggingFilter е филтър, който предоставя основни функции на регистриране. Подкласовете трябва да заменят методите beforeRequest () и afterRequest () , за да извършат действителното регистриране около заявката.

Spring framework предоставя три конкретни класа на изпълнение, които могат да се използват за регистриране на входящата заявка. Тези три класа са:

  • CommonsRequestLoggingFilter
  • Log4jNestedDiagnosticContextFilter (оттеглено)
  • ServletContextRequestLoggingFilter

Сега нека да преминем към CommonsRequestLoggingFilter и да го конфигурираме да улавя входящата заявка за регистриране.

5.1. Конфигурирайте приложението Spring Boot

Приложението Spring Boot може да бъде конфигурирано чрез добавяне на дефиниция на боб, за да се позволи регистриране на заявки:

@Configuration public class RequestLoggingFilterConfig { @Bean public CommonsRequestLoggingFilter logFilter() { CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter(); filter.setIncludeQueryString(true); filter.setIncludePayload(true); filter.setMaxPayloadLength(10000); filter.setIncludeHeaders(false); filter.setAfterMessagePrefix("REQUEST DATA : "); return filter; } }

Също така, този филтър за регистриране изисква нивото на регистрационния файл да бъде зададено на DEBUG. Можем да активираме режима DEBUG, като добавим елемента по-долу в logback.xml :

Друг начин за активиране на дневника на ниво DEBUG е добавянето на следното в application.properties :

logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter= DEBUG

5.2. Конфигурирайте традиционното уеб приложение

In the standard Spring web application, Filter can be set via either XML configuration or Java configuration. Let's set up the CommonsRequestLoggingFilter using conventional Java based configuration.

As we know, the includePayload attribute of CommonsRequestLoggingFilter is set to false by default. We would need a custom class to override the value of the attribute to enable includePayload before injecting into the container using Java configuration:

public class CustomeRequestLoggingFilter extends CommonsRequestLoggingFilter { public CustomeRequestLoggingFilter() { super.setIncludeQueryString(true); super.setIncludePayload(true); super.setMaxPayloadLength(10000); } }

Now, we need to inject the CustomeRequestLoggingFilter using Java based web initializer:

public class CustomWebAppInitializer implements WebApplicationInitializer { public void onStartup(ServletContext container) { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.setConfigLocation("com.baeldung"); container.addListener(new ContextLoaderListener(context)); ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(context)); dispatcher.setLoadOnStartup(1); dispatcher.addMapping("/"); container.addFilter("customRequestLoggingFilter", CustomeRequestLoggingFilter.class) .addMappingForServletNames(null, false, "dispatcher"); } }

6. Example in Action

Now, we can wire up a Spring Boot with context and see in action that logging of incoming requests works as expected:

@Test public void givenRequest_whenFetchTaxiFareRateCard_thanOK() { TestRestTemplate testRestTemplate = new TestRestTemplate(); TaxiRide taxiRide = new TaxiRide(true, 10l); String fare = testRestTemplate.postForObject( URL + "calculate/", taxiRide, String.class); assertThat(fare, equalTo("200")); }

7. Conclusion

В тази статия показахме как да приложим основно регистриране на уеб заявки с помощта на прехващачи; ние също показахме ограниченията и предизвикателствата на това решение.

След това показахме вградения клас на филтър, който осигурява готов за използване и опростен механизъм за регистриране.

Както винаги, изпълнението на примера и кодови фрагменти са достъпни в GitHub.