Cách tạo một bộ lọc trong Spring Boot – Spring Boot Filter

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

https://www.baeldung.com/spring-boot-add-filter

2.5 4 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x