[JAVA] Essayez d'implémenter un filtre WebFlux

Nous continuerons à parler de WebFlux. Jusqu'à la dernière fois, nous avons principalement introduit l'implémentation de la programmation réactive dans la couche Controller-Service.

Cette fois, nous présenterons un exemple d'implémentation d'un filtre utilisé comme traitement commun avant et après une requête.

Implémenter WebFliter au lieu de Filter

Spring MVC implémente javax.servlet.Filter, tandis que WebFlux implémente ʻorg.springframework.web.server.WebFilter`.

Ci-dessous, j'ai implémenté chaque filtre simple qui ne produit que les journaux avant et après le traitement des demandes. Voyons la différence.

Exemple d'implémentation Spring MVC Filter

Filtre qui génère les journaux avant et après le traitement des demandes



@Slf4j
public class TimestampFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest)request;
        // before
        doBeforeRequest(req);
        try {
            // do request
            chain.doFilter(request, response);
            // after (success)
            doAfterRequest(req);
        } catch (Throwable throwable) {
            // after (with error)
            doAfterRequestWithError(req, throwable);
            throw throwable;
        }
    }

    private void doBeforeRequest(HttpServletRequest request) {
        String uri = request.getRequestURI();
        long start = System.currentTimeMillis();
        // start log(ex: /hoge [IN]: 1582989973940)
        log.info(String.format("%s [IN]: %d", uri, start));
    }

    private void doAfterRequest(HttpServletRequest request) {
        String uri = request.getRequestURI();
        long end = System.currentTimeMillis();
        // end log(ex: /hoge [OUT]: 1582989974053)
        log.info(String.format("%s [OUT]: %d", uri, end));
    }

    private void doAfterRequestWithError(HttpServletRequest request, Throwable throwable) {
        String uri = request.getRequestURI();
        long end = System.currentTimeMillis();
        // end with error log(ex: /hoge [OUT]: 1582989974053 with Error(java.lang.RuntimeException))
        log.info(String.format("%s [OUT]: %d with Error(%s)", uri, end, throwable.toString()));
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }
}

Voici le processus équivalent implémenté par WebFilter.

Exemple d'implémentation de Spring WebFlux WebFilter

Filtre qui génère les journaux avant et après le traitement des demandes (WebFilter)



@Slf4j
@Component
public class TimestampFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        return chain.filter(exchange).transformDeferred(call -> doFilter(exchange, call));
    }

    private Publisher<Void> doFilter(ServerWebExchange exchange, Mono<Void> call) {
        // before
        return Mono.fromRunnable(() -> doBeforeRequest(exchange))
                // do request
                .then(call)
                // after (success)
                .doOnSuccess((done) -> doAfterRequest(exchange))
                // after (with error)
                .doOnError((throwable -> doAfterRequestWithError(exchange, throwable)));
    }

    private void doBeforeRequest(ServerWebExchange exchange) {
        String uri = exchange.getRequest().getURI().toString();
        long start = System.currentTimeMillis();
        // start log(ex: /hoge [IN]: 1582989973940)
        log.info(String.format("%s [IN]: %d", uri, start));
    }

    private void doAfterRequest(ServerWebExchange exchange) {
        String uri = exchange.getRequest().getURI().toString();
        long end = System.currentTimeMillis();
        // end log(ex: /hoge [OUT]: 1582989974053)
        log.info(String.format("%s [OUT]: %d", uri, end));
    }

    private void doAfterRequestWithError(ServerWebExchange exchange, Throwable throwable) {
        String uri = exchange.getRequest().getURI().toString();
        long end = System.currentTimeMillis();
        // end with error log(ex: /hoge [OUT]: 1582989974053 with Error(java.lang.RuntimeException))
        log.info(String.format("%s [OUT]: %d with Error(%s)", uri, end, throwable.toString()));
    }
}

(Mono # compose (Function) est obsolète, il a donc été modifié en Mono # transformDeferred (Function). 2020/04/01)

Mono.fromRunnable (~) traite de manière réactive un processus sans valeur de retour (void), et décrit le processus suivant dans then (~). Dans l'exemple, .then (call) est spécifié et le traitement est transféré au filtre ou contrôleur suivant.

De plus, le post-traitement est implémenté en connectant ce qui suit avec doOnSuccess (~) et doOnError (~).

Enfin, jetons un coup d'œil à un exemple d'implémentation de filtre légèrement plus pratique.

Exemple d'implémentation d'un filtre d'authentification personnalisé

Voici un exemple d'implémentation d'un soi-disant filtre de vérification d'authentification qui vérifie si un utilisateur a été authentifié lors de l'accès à une ressource protégée.

Filtre de vérification d'authentification personnalisé



@Slf4j
@Component
@Order(1)
public class AuthFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        return chain.filter(exchange).transformDeferred(call -> doFilter(exchange, call));
    }

    private Publisher<Void> doFilter(ServerWebExchange exchange, Mono<Void> call) {
        return exchange.getSession().flatMap(webSession -> {
            Map<String, Object> attributes = webSession.getAttributes();
            //Obtenir les informations utilisateur de la session
            Optional<User> optionalUser =
                    Optional.ofNullable((User)attributes.get(User.class.getName()));
            return optionalUser
                    //Traiter la demande s'il y a un utilisateur
                    .map(user -> call)
                    //Lancer une erreur non authentifiée si aucun utilisateur
                    .orElseThrow(UnauthorizedException::new);
        });
    }
}

Obtenir les informations de la première session ʻexchange.getSession () retourne Mono `, donc l'écriture suivante est le [Multiple Sequential API Calls] précédemment introduit (https://qiita.com/kilvis/items) / fb18be963da6cac03ee9 #% E8% A4% 87% E6% 95% B0% E3% 81% AE% E9% A0% 86% E6% AC% A1api% E3% 82% B3% E3% 83% BC% E3% 83% Je pense qu'AB) sera utile.

De plus, dans cet exemple, une erreur personnalisée (UnauthorizedException) est générée lorsqu'elle n'est pas authentifiée, il est donc supposé qu'un ExceptionHandler commun le convertira en une réponse 401: Unauthrized.

Si vous souhaitez générer la réponse dans le filtre, vous pouvez l'implémenter comme suit.

Générer une réponse dans WebFilter


    ...réduction...

    private Publisher<Void> doFilter(ServerWebExchange exchange, Mono<Void> call) {
        return exchange.getSession().flatMap(webSession -> {
            Map<String, Object> attributes = webSession.getAttributes();
            //Obtenir les informations utilisateur de la session
            Optional<User> optionalUser =
                    Optional.ofNullable((User)attributes.get(User.class.getName()));
            return optionalUser
                    //Traiter la demande s'il y a un utilisateur
                    .map(user -> call) // do request
                    //Générer une réponse d'erreur non authentifiée si aucun utilisateur
                    .orElse(writeUnauthorizedResponse(exchange));
        });
    }

    private Mono<Void> writeUnauthorizedResponse(ServerWebExchange exchange) {
        // 401:Générer une réponse JSON en tant qu'erreur non autorisée
        ServerHttpResponse response = exchange.getResponse();
        String body = "{\"message\":\"L'authentification de connexion est requise.\"}";
        return writeResponse(response, HttpStatus.UNAUTHORIZED, body);
    }

    private Mono<Void> writeResponse(ServerHttpResponse response, HttpStatus status, String jsonBody) {
        response.setStatusCode(status);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
        DataBufferFactory dbf = response.bufferFactory();
        return response.writeWith(Mono.just(dbf.wrap(jsonBody.getBytes())));
    }

Ce résumé

Filtres dans WebFlux

--Implémenté en tant que sous-classe de WebFilter

Ce sera.

Recommended Posts

Essayez d'implémenter un filtre WebFlux
Essayez d'implémenter une session WebFlux
[Java] Créer un filtre
Essayez d'envoyer une notification.
Essayez d'implémenter Yuma dans Kinx
Essayez le tutoriel Spring WebFlux
Essayez d'implémenter Android Hilt en Java
Essayez d'implémenter le traitement asynchrone dans Azure
Essayez de faire un simple rappel
Essayez d'implémenter recaptcha avec Jetty intégré.
Essayez Spring WebFlux (principalement les fonctions de routeur)
Essayez de créer une application client serveur
Essayez de créer un itérateur qui puisse être vu