[JAVA] Versuchen Sie, einen WebFlux-Filter zu implementieren

Wir werden weiterhin über WebFlux sprechen. Bis zum letzten Mal haben wir hauptsächlich die Implementierung der reaktiven Programmierung in der Controller-Service-Schicht eingeführt.

Dieses Mal werden wir ein Implementierungsbeispiel eines Filters vorstellen, der vor und nach einer Anforderung als allgemeine Verarbeitung verwendet wird.

Implementieren Sie WebFliter anstelle von Filter

Spring MVC implementiert "javax.servlet.Filter", während WebFlux "org.springframework.web.server.WebFilter" implementiert.

Im Folgenden habe ich jeden einfachen Filter implementiert, der nur Protokolle vor und nach der Anforderungsverarbeitung ausgibt. Mal sehen, den Unterschied.

Beispiel für die Implementierung eines Spring MVC-Filters

Filter, der Protokolle vor und nach der Anforderungsverarbeitung ausgibt



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

Das Folgende ist der äquivalente Prozess, der von WebFilter implementiert wird.

Beispiel für die Implementierung von Spring WebFlux WebFilter

Filter, der Protokolle vor und nach der Anforderungsverarbeitung ausgibt (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 (Funktion) ist veraltet, daher wurde es in Mono # transformDeferred (Funktion) geändert. 2020/04/01)

Mono.fromRunnable (~) verarbeitet reaktiv einen Prozess ohne Rückgabewert (void) und beschreibt den nachfolgenden Prozess in then (~). Im Beispiel wird .then (call) angegeben und die Verarbeitung an den nächsten Filter oder Controller übertragen.

Darüber hinaus wird die Nachbearbeitung implementiert, indem Folgendes mit "doOnSuccess (~)" und "doOnError (~)" verbunden wird.

Schauen wir uns zum Schluss ein etwas praktischeres Beispiel für die Filterimplementierung an.

Implementierungsbeispiel für einen benutzerdefinierten Authentifizierungsfilter

Das Folgende ist eine Beispielimplementierung eines sogenannten Authentifizierungsprüfungsfilters, der prüft, ob ein Benutzer beim Zugriff auf eine geschützte Ressource authentifiziert wurde.

Filter für benutzerdefinierte Authentifizierungsprüfung



@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();
            //Benutzerinformationen aus der Sitzung abrufen
            Optional<User> optionalUser =
                    Optional.ofNullable((User)attributes.get(User.class.getName()));
            return optionalUser
                    //Prozessanforderung, wenn ein Benutzer vorhanden ist
                    .map(user -> call)
                    //Wirf einen nicht authentifizierten Fehler aus, wenn kein Benutzer vorhanden ist
                    .orElseThrow(UnauthorizedException::new);
        });
    }
}

Das Abrufen der ersten Sitzungsinformationen exchange.getSession () gibtMono <WebSession>zurück, sodass das nachfolgende Schreiben die zuvor eingeführten Multiple Sequential API Calls ist. / fb18be963da6cac03ee9 #% E8% A4% 87% E6% 95% B0% E3% 81% AE% E9% A0% 86% E6% AC% A1api% E3% 82% B3% E3% 83% BC% E3% 83% Ich denke AB) wird hilfreich sein.

In diesem Beispiel wird außerdem ein benutzerdefinierter Fehler (UnauthorizedException) ausgelöst, wenn er nicht authentifiziert ist. Daher wird davon ausgegangen, dass der allgemeine ExceptionHandler ihn in eine Antwort "401: Unauthrized" konvertiert.

Wenn Sie die Antwort im Filter generieren möchten, können Sie sie wie folgt implementieren.

Antwort in WebFilter generieren


    ...Kürzung...

    private Publisher<Void> doFilter(ServerWebExchange exchange, Mono<Void> call) {
        return exchange.getSession().flatMap(webSession -> {
            Map<String, Object> attributes = webSession.getAttributes();
            //Benutzerinformationen aus der Sitzung abrufen
            Optional<User> optionalUser =
                    Optional.ofNullable((User)attributes.get(User.class.getName()));
            return optionalUser
                    //Prozessanforderung, wenn ein Benutzer vorhanden ist
                    .map(user -> call) // do request
                    //Generieren Sie eine nicht authentifizierte Fehlerantwort, wenn kein Benutzer vorhanden ist
                    .orElse(writeUnauthorizedResponse(exchange));
        });
    }

    private Mono<Void> writeUnauthorizedResponse(ServerWebExchange exchange) {
        // 401:Generieren Sie eine JSON-Antwort als nicht autorisierten Fehler
        ServerHttpResponse response = exchange.getResponse();
        String body = "{\"message\":\"Eine Login-Authentifizierung ist erforderlich.\"}";
        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())));
    }

Diese Zusammenfassung

Es wird sein.

Recommended Posts

Versuchen Sie, einen WebFlux-Filter zu implementieren
Versuchen Sie, eine WebFlux-Sitzung zu implementieren
[Java] Erstellen Sie einen Filter
Versuchen Sie, eine Benachrichtigung zu senden.
Versuchen Sie, Yuma in Kinx zu implementieren
Probieren Sie das Spring WebFlux-Tutorial aus
Versuchen Sie, Android Hilt in Java zu implementieren
Versuchen Sie, die asynchrone Verarbeitung in Azure zu implementieren
Versuchen Sie, einen einfachen Rückruf zu tätigen
Versuchen Sie, recaptcha mit eingebettetem Jetty zu implementieren.
Probieren Sie Spring WebFlux aus (hauptsächlich Router-Funktionen).
Versuchen Sie, eine Server-Client-App zu erstellen
Versuchen Sie, einen Iterator zu erstellen, der einen Blick darauf werfen kann