DéveloppeurWeb.Com
    DéveloppeurWeb.Com
    • Agile Zone
    • AI Zone
    • Cloud Zone
    • Database Zone
    • DevOps Zone
    • Integration Zone
    • Web Dev Zone
    DéveloppeurWeb.Com
    Home»Web Dev Zone»Activation de la fonctionnalité de démarrage angulaire/printemps par profil de printemps
    Web Dev Zone

    Activation de la fonctionnalité de démarrage angulaire/printemps par profil de printemps

    octobre 19, 2021
    Activation de la fonctionnalité de démarrage angulaire/printemps par profil de printemps
    Share
    Facebook Twitter Pinterest Reddit WhatsApp Email

    Description du cas d’utilisation

    Parfois, il existe un cas d’utilisation pour un système qui a les mêmes exigences de base mais qui a besoin d’une logique différente dans le noyau pour différents clients. La question est : les différences justifient-elles un nouveau microservice ou mettez-vous la même logique ? En gros, la question est : un monolithe ou un zoo de microservices ?

    La réponse dépend des exigences et les deux extrêmes sont probablement faux.

    Pour des exigences très similaires, la fonctionnalité Spring pour filtrer les classes peut être utilisée avec une condition basée sur le profil sélectionné. Les profils sont une liste séparée par des virgules qui peut être utilisée pour décider quelles classes filtrer. Pour Spring, les classes ne sont alors pas disponibles et seuls les points de terminaison et les services nécessaires peuvent être injectés.

    L’interface angulaire peut obtenir un ConfigService qui renvoie les profils utilisés pour activer les fonctionnalités basées sur eux. Le routage peut également être basé sur les profils et utiliser des modules à chargement paresseux Angulars.

    Cela permet de conserver les parties communes et d’ajouter des fonctionnalités différentes pour chaque client. Mais il faut un avertissement :

    Si vous n’avez pas de bonnes informations sur le nombre d’exigences différentes qui viendront au fil du temps, pensez à davantage de petits microservices. Ensuite, vous n’avez pas à expliquer pourquoi, après un certain moment, l’application est devenue trop volumineuse et vous devez la scinder maintenant. Il s’agit plus d’une considération culturelle que technique.

    Implémentation du backend

    Le projet utilisé comme exemple est AngularPortfolioMgr. Les profils ‘dev’ et ‘prod’ sont utilisés dans cet exemple mais d’autres profils peuvent être ajoutés et utilisés. Les classes séparées sont dans des packages qui sont préfixés ‘dev’ ou ‘prod’. Le filtre est implémenté dans la classe ComponentScanCustomFilter :

    public class ComponentScanCustomFilter implements TypeFilter, EnvironmentAware {
      private Environment environment;
    
      @Override
      public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        String searchString = List.of(this.environment.getActiveProfiles())
          .stream().anyMatch(profileStr -> profileStr.contains("prod")) ? 
             "ch.xxx.manager.dev." : "ch.xxx.manager.prod.";
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        return classMetadata.getClassName().contains(searchString);
      }
    
      @Override
      public void setEnvironment(Environment environment) {
        this.environment = environment;
      }
    }

    Le setter en bas implémente EnvironmentAware et obtient l’environnement injecté à ce stade précoce du démarrage de l’application. La méthode match implémente le Typefilter et définit le filtre de classe en fonction du profil de l’environnement. True est renvoyé pour les classes exclues.

    Le filtre de classe est utilisé dans la classe ManagerApplication :

    @SpringBootApplication
    @ComponentScan(basePackages = "ch.xxx.manager", excludeFilters = 
    @Filter(type = FilterType.CUSTOM, classes = ComponentScanCustomFilter.class))
    public class ManagerApplication {
      public static void main(String[] args) {
        SpringApplication.run(ManagerApplication.class, args);
      }
    }

    L’annotation ‘@ComponentScan’ définit les basesPackages où fonctionne le filer. La propriété ‘excludeFilters’ ajoute le ComponentScanCustomFilter. Cela exclut les packages avec ‘dev.*’ ou ‘prod.*’. Cela rend impossible l’utilisation de classes dans ces packages.

    Pour obtenir un code de retour http 401 pour les points de terminaison ‘/rest/dev’ ou ‘/rest/prod’ exclus, la configuration Spring Security dans la classe WebSecurityConfig doit être mise à jour :

    ...
    private static final String DEVPATH="/rest/dev/**";
    private static final String PRODPATH="/rest/prod/**";
    @Value("${spring.profiles.active:}")
    private String activeProfile;
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
      final String blockedPath = this.activeProfile.toLowerCase()
    .contains("prod") ? DEVPATH : PRODPATH;
      http.httpBasic().and().sessionManagement()
       .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
       .and().authorizeRequests()		
       .antMatchers("/rest/config/**").permitAll()
       .antMatchers("/rest/auth/**").permitAll()
       .antMatchers("/rest/**").authenticated()
       .antMatchers(blockedPath).denyAll()
       .antMatchers("/**").permitAll()
       .anyRequest().authenticated()
       .and().csrf().disable()
       .headers().frameOptions().sameOrigin()
       .and().apply(new JwtTokenFilterConfigurer(jwtTokenProvider));
    }
    ...

    Le ‘blockedPath’ est défini en fonction de l’activeProfile. Il définit ensuite le chemin exclu sur ‘denyAll()’ avec un AntMatcher. Le point de terminaison du ConfigController(‘/rest/config/profiles’) est défini sur ‘permitAll()’ pour permettre un accès anticipé à la valeur. Les chemins ajoutés héritent de la valeur ‘authenticated()’.

    Le ConfigController fournit le point de terminaison que le frontend utilise pour obtenir les profils.

    Les différentes exigences peuvent être implémentées dans des classes telles que ProdAppInfoService dans les packages ‘prod’ et DevAppInfoService dans les packages ‘dev’. Les différents points de terminaison peuvent être implémentés dans des classes comme le ProdAppInfoController dans les packages ‘prod’ et le DevAppInfoController dans les packages ‘dev’.

    ArchUnit peut être utilisé pour vérifier les dépendances du package pendant la construction. Une configuration peut être trouvée dans la classe MyArchitectureTests.

    Implémentation du frontend

    L’interface angulaire a un ConfigService qui récupère puis met en cache les profils :

    @Injectable({
      providedIn: 'root'
    })
    export class ConfigService {
      private profiles: string = null;
      constructor(private http: HttpClient) { }
    
      getProfiles(): Observable<string> {		
        if(!this.profiles) {			
          return this.http.get(`/rest/config/profiles`, 
            {responseType: 'text'})
              .pipe(tap(value => this.profiles = value));
        } else {
          return of(this.profiles);
        }
      }
    }

    Le ConfigService récupère les profils une fois, puis les met en cache pour toujours.

    Le ConfigService est utilisé dans OverviewComponent. La méthode ‘ngOnInit’ initialise la propriété ‘profiles’ :

    ...
    private profiles: string = null;
    ...
    ngOnInit() {
      ...
      this.configService.getProfiles()
        .subscribe(value => this.profiles = !value ? 'dev' : value);
    }
    ...

    Le configService injecté est utilisé pour obtenir les profils. Si le résultat est vide, la valeur ‘dev’ est utilisée sinon la valeur renvoyée.

    La méthode ‘showConfig()’ du OverviewComponent ouvre une boîte de dialogue selon la propriété ‘profiles’ :

    showConfig(): void {
      if (!this.dialogRef && this.profiles) {
        if(!!this.dialogSubscription) {
          this.dialogSubscription.unsubscribe();                
        }
        const myOptions = { width: '700px' };
        this.dialogRef = this.profiles.toLowerCase().includes('prod') ? 
          this.dialog.open(ProdConfigComponent, myOptions) 
          : this.dialog.open(DevConfigComponent, myOptions);
        this.dialogSubscription = this.dialogRef.afterClosed().subscribe(() => this.dialogRef = null);
      }
    }

    La méthode ‘showConfig()’ vérifie d’abord qu’aucune boîte de dialogue n’est ouverte et que la propriété ‘profiles’ est définie. Puis est coché si l’abonnement dialogSubscription est coché et désabonné s’il est défini. Ensuite, il ouvre la boîte de dialogue avec le ProdConfigComponent ou le DevConfigComponent selon la propriété ‘profils’.

    Le ProdConfigComponent injecte le ProdAppInfoService pour récupérer le nom de classe du point de terminaison ‘/rest/prod/app-info/class-name’ et l’affiche.

    Le DevConfigComponent injecte le DevAppInfoService pour récupérer le nom de classe du point de terminaison ‘/rest/dev/app-info/class-name’ et l’affiche.

    Les différences dans le frontend peuvent être implémentées comme dans cet exemple. Il est possible de router selon les profils du ConfigService et d’utiliser des modules chargés paresseux dans Angular. Dans AppRoutingModule, des modules chargés paresseux sont utilisés. Ensuite, l’utilisateur ne charge que les modules angulaires nécessaires à la configuration.

    Conclusion

    L’activation de fonctionnalités avec Spring Profiles permet de séparer en toute sécurité les différentes exigences dans une application. Seules les fonctionnalités requises sont disponibles et les fonctionnalités frontend peuvent également être activées en fonction des besoins. Les modules angulaires à chargement paresseux permettraient aux utilisateurs de ne charger que les pièces nécessaires à leur configuration. Cette conception est utile si les différences dans les exigences sont faibles (et le resteront) et qu’il doit y avoir une séparation nette.

    Share. Facebook Twitter Pinterest LinkedIn WhatsApp Reddit Email
    Add A Comment

    Leave A Reply Cancel Reply

    Catégories

    • Politique de cookies
    • Politique de confidentialité
    • CONTACT
    • Politique du DMCA
    • CONDITIONS D’UTILISATION
    • Avertissement
    © 2023 DéveloppeurWeb.Com.

    Type above and press Enter to search. Press Esc to cancel.