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.
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.
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.
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.
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
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())));
}
Filtres dans WebFlux
--Implémenté en tant que sous-classe de WebFilter
Ce sera.
Recommended Posts