¿Cómo hacer una secuencia de operaciones y asegurar que una operación se complete antes de la siguiente en la aplicación web Spring Reactor?

Tengo la aplicación web Spring Boot 2 en la que necesito identificar al visitante del sitio mediante cookies y recopilar estadísticas de visualización de la página. Entonces necesito interceptar cada solicitud web. El código que tuve que escribir es más complejo que devolver la llamada al infierno (el problema que se suponía que el reactor de Spring debía resolver).

Aquí está el código:

package mypack.conf;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
import org.springframework.http.HttpCookie;
import org.springframework.http.ResponseCookie;
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;

import mypack.dao.PageViewRepository;
import mypack.dao.UserRepository;
import mypack.domain.PageView;
import mypack.domain.User;
import mypack.security.JwtProvider;

import reactor.core.publisher.Mono;
@Configuration


@ComponentScan(basePackages = "mypack")
@EnableReactiveMongoRepositories(basePackages = "mypack")
public class WebConfig implements WebFluxConfigurer {

    @Autowired
    @Lazy
    private UserRepository userRepository;

    @Autowired
    @Lazy
    private PageViewRepository pageViewRepository;


    @Autowired
    @Lazy
    JwtProvider jwtProvider;


    @Bean
    public WebFilter sampleWebFilter()  {
        return new WebFilter() {

            @Override
            public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {

                String uri = exchange.getRequest().getURI().toString();
                String path = exchange.getRequest().getPath().pathWithinApplication().value();


                HttpCookie  cookie = null;
                String token = "";
                Map<String, List<HttpCookie>> cookies = exchange.getRequest().getCookies();


                try {
                    if((exchange.getRequest().getCookies().containsKey("_token") )
                            &&  (exchange.getRequest().getCookies().getFirst("_token"))!=null  ) {

                        cookie = exchange.getRequest().getCookies().getFirst("_token");
                        token = cookie.getValue();


                        return userRepository.findByToken(token).map(user -> {

                                exchange.getAttributes().put("_token", user.getToken());


                                PageView pg = PageView.builder().createdDate(LocalDateTime.now()).URL(uri).build();
                                pageViewRepository.save(pg).subscribe(pg1 -> {user.getPageviews().add(pg1); });

                                userRepository.save(user).subscribe();
                                    return user;
                            })


                            .flatMap(user-> chain.filter(exchange)); // ultimately this step executes regardless user exist or not

                    // handle case when brand new user first time visits website    
                    } else {
                        token = jwtProvider.genToken("guest", UUID.randomUUID().toString());
                        User user = User.builder().createdDate(LocalDateTime.now()).token(token).emailId("guest").build();
                        userRepository.save(user).subscribe();
                        exchange.getResponse().getCookies().remove("_token");

                        ResponseCookie rcookie  = ResponseCookie.from("_token", token).httpOnly(true).build();
                        exchange.getResponse().addCookie(rcookie);
                        exchange.getAttributes().put("_token", token);

                    }

                } catch (Exception e) {

                    e.printStackTrace();
                }



                return chain.filter(exchange);
            } // end of  Mono<Void> filter method
        }; // end of New WebFilter (anonymous class)
    }

}

Otras clases relevantes:

@Repository
public interface PageViewRepository extends   ReactiveMongoRepository<PageView, String>{

    Mono<PageView> findById(String id);

}


@Repository
public interface UserRepository extends   ReactiveMongoRepository<User, String>{

    Mono<User> findByToken(String token);

}





@Data
@AllArgsConstructor
@Builder
@NoArgsCo,nstructor
public class User {

    @Id
    private String id;
    private String token;


    @Default
    private LocalDateTime createdDate = LocalDateTime.now();

    @DBRef
    private List<PageView> pageviews;

}



Data
@Document
@Builder
public class PageView {
    @Id
    private String id;

    private String URL;

    @Default
    private LocalDateTime createdDate = LocalDateTime.now();
}

Parte relevante del archivo gradle:

buildscript {
    ext {

        springBootVersion = '2.0.1.RELEASE'
    }
    repositories {
        mavenCentral()

    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

dependencies {

    compile('org.springframework.boot:spring-boot-starter-data-mongodb-reactive')
    compile('org.springframework.boot:spring-boot-starter-security')
    compile('org.springframework.boot:spring-boot-starter-thymeleaf')

    compile('org.springframework.boot:spring-boot-starter-webflux')

    compile('org.springframework.security:spring-security-oauth2-client')
    compile('org.springframework.security.oauth:spring-security-oauth2:2.3.4.RELEASE')
    runtime('org.springframework.boot:spring-boot-devtools')
    compileOnly('org.projectlombok:lombok')
    compile "org.springframework.security:spring-security-jwt:1.0.9.RELEASE"
    compile "io.jsonwebtoken:jjwt:0.9.0"

    testCompile('org.springframework.boot:spring-boot-starter-test')

    testCompile('io.projectreactor:reactor-test')

    compile('com.fasterxml.jackson.core:jackson-databind')
}

El problema está en estas líneas:

PageView pg = PageView.builder (). CreatedDate (LocalDateTime.now ()). URL (uri) .build (); pageViewRepository.save (pg) .subscribe (pg1 -> {user.getPageviews (). add (pg1);});

que cuelga el navegador (sigue esperando respuesta).

Básicamente lo que quiero es esto: no debe usar block (), que ni siquiera funciona en el código de filtro web, ya que block también bloquea el navegador. Guardar página vista en mongo db. Después de guardarlo, la página vista tiene una identificación mongodb válida que se necesita para ser almacenada como referencia en las páginas vistas Lista de la entidad del usuario. Por lo tanto, solo después de guardarlo en db, el siguiente paso es actualizar la Lista de páginas vistas del usuario. El siguiente paso es guardar al usuario actualizado sin afectar los métodos de controlador posteriores que también pueden actualizar al usuario y pueden necesitar guardarlo también. Todo esto debería funcionar en el contexto WebFilter dado.

¿Cómo resolver este problema

La solución proporcionada debe asegurarse de que el usuario se guarde en el filtro web antes de pasar a las acciones del controlador, algunas de las cuales también guardan al usuario con diferentes valores de los parámetros de cadena de consulta.

Respuestas a la pregunta(3)

Su respuesta a la pregunta