Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu cách tạo ra các filter và chỉ định thứ tự gọi của chúng trong Spring Boot.
Định nghĩa Spring Filter
Giả sử chúng ta tạo 2 fillter sau:
- TransactionFilter – dùng để start và commit một transaction
- RequestResponseLoggingFilter – dùng dể log các request và response.
Giả sử có Book class dùng trong các ví dụ dưới đây
package com.deft.entity; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class Book { private String name; private Integer price; private String author; }
Để sắp xếp thứ tự các filter chúng ta có thể sử dụng @Order annotation để chỉ định thứ tự gọi cho mỗi Filter.
Để tạo một Filter trong Spring Boot chúng ta cần tạo class implements Filter interface.
package com.deft.filter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @Component @Order(1) public class TransactionFilter implements Filter { private final static Logger LOG = LoggerFactory.getLogger(TransactionFilter.class); @Override public void init(final FilterConfig filterConfig) throws ServletException { LOG.info("Initializing filter :{}", this); } @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; LOG.info("Starting Transaction for req :{}", req.getRequestURI()); chain.doFilter(request, response); LOG.info("Committing Transaction for req :{}", req.getRequestURI()); } @Override public void destroy() { LOG.warn("Destructing filter :{}", this); } }
Và RequestResponseLoggingFilter
package com.deft.filter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component @Order(2) public class RequestResponseLoggingFilter implements Filter { private final static Logger LOG = LoggerFactory.getLogger(RequestResponseLoggingFilter.class); @Override public void init(final FilterConfig filterConfig) throws ServletException { LOG.info("Initializing filter :{}", this); } @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; LOG.info("Logging Request {} : {}", req.getMethod(), req.getRequestURI()); chain.doFilter(request, response); LOG.info("Logging Response :{}", res.getContentType()); } @Override public void destroy() { LOG.warn("Destructing filter :{}", this); } }
Lưu ý để Spring có thể tìm và khởi tạo bean cho các Filter mà chúng ta đã khởi tạo thì cần phải chú thích nó với @Component annotation.
Để kiểm tra các Filter chúng ta đã hoạt động hay chưa thì mình sẽ tiến hành tại một BookController nhận các request từ client gửi đến
package com.deft.controller; import com.deft.entity.Book; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @RestController @RequestMapping("/books") public class BookController { @GetMapping public List<Book> findAll() { List<Book> books = new ArrayList<>(); books.add(new Book("Spring Booot", 13, "Deft")); books.add(new Book("Java Core", 16, "Deft")); return books; } }
Khởi chạy và kiểm thử với CURL
curl http://localhost:8080/books --- [ { "name": "Spring Booot", "price": 13, "author": "Deft" }, { "name": "Java Core", "price": 16, "author": "Deft" } ]
Đồng thời chúng ta có thể thấy màn hình console xuất ra dữ liệu tương ứng với các Filter mà chúng ta đã cài đặt, tất nhiên là chúng phải chạy theo thứ tự định sẵn.
com.deft.filter.TransactionFilter : Starting Transaction for req :/books c.d.filter.RequestResponseLoggingFilter : Logging Request GET : /books c.d.filter.RequestResponseLoggingFilter : Logging Response :application/json com.deft.filter.TransactionFilter : Committing Transaction for req :/books
Filter với URL Pattern
Ở ví dụ trên, các Filter mà chúng ta tạo ra sẽ hoạt động cho tất cả các URL trong ứng dụng. Tuy nhiên, đôi lúc bạn muốn những Filter này chỉ hoạt động cho các URL cụ thể hoặc theo một cấu trúc nhất định.
Trong trường hợp này, chúng ta phải xoá @Component annotation ra khởi Filter class và đăng ký chúng thông qua FilterRegistrationBean sử dụng @Configuration class.
Giả sử chúng ta UserFilter class chỉ hoạt động cho các API liên quan đến user có cấu trúc dạng – /users/*
package com.deft.filter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class UserFilter implements Filter { private final static Logger LOG = LoggerFactory.getLogger(RequestResponseLoggingFilter.class); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; LOG.info("Logging User Request {} : {}", req.getMethod(), req.getRequestURI()); chain.doFilter(request, response); LOG.info("Logging User Response :{}", res.getContentType()); } }
Sau đó sử dụng đăng ký UserFilter thông qua FilterRegistrationBean với đường dẫn được chỉ định
package com.deft.config; import com.deft.filter.UserFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyConfig { @Bean public FilterRegistrationBean<UserFilter> loggingFilter(){ FilterRegistrationBean<UserFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new UserFilter()); registrationBean.addUrlPatterns("/users/*"); return registrationBean; } }
Lúc này chỉ khi có những request đến các API bắt đầu bằng /users thì UserFilter sẽ được khởi chạy
package com.deft.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @RestController @RequestMapping("/users") public class UserController { @GetMapping public List<String> findAll() { return new ArrayList<>(); } }
Output
curl http://localhost:8080/users --- c.d.filter.RequestResponseLoggingFilter : Logging User Request GET : /users c.d.filter.RequestResponseLoggingFilter : Logging User Response :application/json
Kết bài
Qua bài hướng dẫn này hy vọng các bạn đã biết cách tạo và sử dụng Filter trong Spring Boot. Filter có ý nghĩa quan trọng trong các ứng dụng Spring, có thể dùng để kiểm tra logic các request trước khi nó đi vào controller, service. Dùng để log lại các request từ bên ngoài đến v.v
Cuối cùng mã nguồn được mình công khai trên gitlab để các bạn có thể tiện theo dõi – spring-filter.
Nguồn tham khảo