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.
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.
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.
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.
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())));
}
Es wird sein.
Recommended Posts