ゲートウェイはマイクロサービス ドキュメントを統合します: Knife4j ドキュメント リクエスト例外、Swagger エラー API 定義のロードに失敗しました。

今日Gatewayを使ってマイクロサービスドキュメントを統合したところ、Knife4jのドキュメントリクエストが異常だったのでデータパッケージを確認したところ、このようなパスがリクエストされていました。(ストリーム保存アシスタント: エラーの理由は、api-doc の取得方法が間違っているためです。私が何を言っているのか理解できない場合は、以下を読んでください。) 統合されたコードは直接 CV にあります
ここに画像の説明を挿入します
。いくつかの変更を行う必要があるようですが、特に重要なのは、ゲートウェイの 2 つの構成と他のサービスの構成ファイルがスタンドアロン サーバーの構成と一致していることです。ゲートウェイの構成ファイルは次のとおりです。
最初のファイルは Config です。

@Slf4j
@Component
@Primary
@AllArgsConstructor
public class SwaggerResourceConfig implements SwaggerResourcesProvider {
    
    
    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;

    @Override // 请求网关时就会执行此方法
    public List<SwaggerResource> get() {
    
    
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        //获取所有路由的ID并加入到routes里
        routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
        //过滤出配置文件中定义的路由->过滤出Path Route Predicate->根据路径拼接成api-docs路径->生成SwaggerResource
        gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {
    
    
            route.getPredicates().stream()
                    .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
                    .forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(),
                            predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
                                    .replace("**", "v2/api-docs"))));
        });

        return resources;
    }

    private SwaggerResource swaggerResource(String name, String location) {
    
    
        log.info("name:{},location:{}", name, location);
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion("2.0");
        return swaggerResource;
    }
}

2番目はハンドラーです

/**
 * 自定义Swagger的各个配置节点
 */
@RestController
public class SwaggerHandler {
    
    

    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;

    @Autowired(required = false)
    private UiConfiguration uiConfiguration;

    private final SwaggerResourcesProvider swaggerResources;

    @Autowired
    public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
    
    
        this.swaggerResources = swaggerResources;
    }

    /**
     * Swagger安全配置,支持oauth和apiKey设置
     */
    @GetMapping("/swagger-resources/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
    
    
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    /**
     * Swagger UI配置
     */
    @GetMapping("/swagger-resources/configuration/ui")
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
    
    
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    /**
     * Swagger资源配置,微服务中这各个服务的api-docs信息
     */
    @GetMapping("/swagger-resources")
    public Mono<ResponseEntity> swaggerResources() {
    
    
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
}

エラー箇所

コンソールのネットワーク要求レコードから、最初にハンドラーの swagger-resource を要求して API-docs を取得し、次に API-docs を要求したことがわかります。
ここに画像の説明を挿入します
api-doc は、以下の図の青い URL (http:localhost:10000/v2/api-docs) を指します。この API-docs は、Postman やその他の API テスト ツールにドキュメントをインポートするためにも使用でき、非常に便利です。
ここに画像の説明を挿入します
SwaggerHandler が swagger-resource リクエストを受信すると、自動的に挿入された swaggerResources get メソッドが呼び出されます。この get メソッドは SwaggerResourceConfig で書き換えられるため、この get メソッドのポイントを壊します。
get メソッドは、自動挿入を通じてゲートウェイの RouteLocator と GatewayProperties を取得します。routeLocator には、デリゲート、ルート、およびキャッシュの 3 つのフィールドが含まれます。
キャッシュでは、ゲートウェイで構成したすべてのルートがチェーンを形成していることがわかります。Java で作成したルーティング設定がチェーン全体の最上位にあることがわかります。
ここに画像の説明を挿入します

以下のソース コードから、routeLocator の getRoutes メソッドが実際に上図の情報をキャッシュから直接並べ替えて返していることがわかります。これらのルートは Flux、つまり応答性の高いフローの形式で編成されているため、subscribe を使用してデータ フローをトリガーし、すべてのルート ID を独自に作成したリストに追加する必要があります。
ここに画像の説明を挿入します
ここでは、gatewaProperties の getRoutes メソッドを通じてルートが再度取得されますが、今回は構成ファイルで静的に宣言されたルートのみが存在することがわかります。
ここに画像の説明を挿入します
次のステップは一連のストリーム処理で、前のステップで抽出したルート ID セットにルート ID が含まれていない設定ファイルをフィルタリングし、残りの各設定ファイルを照合して述語が Path タイプであるかどうかを確認します。それはパスです。その場合、設定したパス値を取り出します。値はハッシュ テーブルに保存され、キーは「_genkey_0」です。この自動生成されたプレフィックスは、NameUtils を通じて取得できます。(詳細な構造については、下図のデバッガー コンソールの変数列を参照してください)
ここに画像の説明を挿入します
データが流出すると、処理後に 6 つの URL を取得したことがわかります。ゲートウェイ マイクロサービスのスワガーはこれらの URL を使用して、 json ファイルを取得し、各マイクロサービスのドキュメントを 1 つのドキュメントに集約します。(下図のデバッガー コンソールの変数列)
ここに画像の説明を挿入します
明らかに、現時点ではゲートウェイの構成が間違っているため、ゲートウェイの Swagger は正しい API ドキュメントを取得できません。したがって、パスマッピングが正しい限りは問題ありません。

解決

  • ルーティング ルールを変更します (StripPrefix はプレフィックスを削除してから転送します)。
  • SwaggerResource を取得するためのルールを変更する

現在の Swagger ドキュメントの場所は、localhost:10000/swagger-ui.html など、各マイクロサービス パスの下のルート パスです。このとき、SwaggerResource を取得するためのルールはパスによって照合されます。明らかに、ルート ディレクトリをマップすることは不可能です。 (ルート ディレクトリと一致させるには、ルート パスのリクエストと一致するようにルーティング ルールを変更する必要があります。これにより、マイクロサービスを区別できないだけでなく、すべてのリクエストがインターセプトされてしまいます)。

また、リソース内で一致するすべてのルート ID がドキュメントに追加されることにも注意してください。そのため、SwaggerResource を取得するためのルールを変更し、/api/xxx で始まるルートを削除する必要があります。

ここに画像の説明を挿入します
したがって、次の図に示すようにルートのセットを追加します。
ここに画像の説明を挿入します
StripPrefix=2 は、転送間のパス、つまり /swagger/ware の最初の 2 つのプレフィックスを削除し、転送パスがルート ディレクトリになります。
そして、getメソッドは次のように変更されます

    @Override // 请求网关时就会执行此方法
    public List<SwaggerResource> get() {
    
    
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        //获取所有路由的ID并加入到routes里
        routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
        //过滤出配置文件中定义的路由->过滤出Path Route Predicate->根据路径拼接成api-docs路径->生成SwaggerResource
        gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {
    
    
            route.getPredicates().stream()
                    .filter(predicateDefinition ->{
    
    
                        boolean condition1 =("Path").equalsIgnoreCase(predicateDefinition.getName());
                        String url = predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0");
                        boolean condition2=false;
                        if(url.length()>9){
    
    
                            condition2 = ("/swagger/").equalsIgnoreCase(url.substring(0,9));
                        }
                        return condition1 && condition2;
                    })
                    .forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(),
                            predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
                                    .replace("**", "v2/api-docs"))));
        });

        return resources;
    }

ハイライト 1: パスマッピング

私はかつて、swagger によって設定された pathMapping を変更することでマッピング パスを変更できるのではないかと考えました。実際、これは Swagger にアクセスするパスには影響しません。つまり、構成ファイルでテストする pathMapping を設定し、http://localhost:88/swagger-ui.htm または http://localhost:88/swagger/ware/v2/api-docs を使用します。今回はjsonデータにアクセスまたは取得できます。http://localhost:88/test/swagger-ui.htm または http://localhost:88/swagger/ware/test/v2/api-docs を使用すると 404 になります

ここに画像の説明を挿入します
では、パスマッピングは何に役立つのでしょうか?
簡単に言えば、この pathMapping は、Swagger テスト インターフェイスを使用してリクエストを送信するときに、このプレフィックスを追加することを指します (プレフィックス テストの図に示すように)。フロントエンド リクエストには通常、プレフィックスとして /api/product が含まれており、このプレフィックスはゲートウェイが使用されるときに除外されます。この機能は、バックエンド インターフェイスを直接リクエストするのではなく、フロントエンド リクエストをシミュレートすることです。

たとえば、次の例を見てください。この時点のベース URL は localhost:88/swagger/ware/ です。localhost:88 はゲートウェイのアドレスであり、プレフィックス /swagger/ware/ はリクエスト時に自動的に
ここに画像の説明を挿入します
削除されます。 forwardedパスは localhost:88/swagger/ware/ware/purchase/info/2 である必要がありますが、pathMapping をテストとして設定したため、baseurl と path の間に追加のテスト (pathMapping) があり、これは swagger を意味します。 リクエスト パスは Baseurl です
。 +パスマッピング+パス
ここに画像の説明を挿入します

ヒント 2: Java でルーティング ルールを記述する方法

前述の通り、javaを使用してルーティング設定を記述しました。ルーティング設定は以下のとおりです。以下のymlで設定したルーティングは同等のルーティングを設定できますが、上記の分析からもリンクの先頭にあることがわかります。RouteLocator は、上で SwaggerConfig で使用したものです。

@Configuration
public class TestConfig {
    
    

    /*
    * 通过RouteLocatorBuilder的routes,可以逐一建立路由,每调用route一次可建立一条路由规则.
    * p的代表是PredicateSpec,可以透过它的predicate来进行断言,要实现的接口就是Java 8的Predicate,
    * 通过exchange取得了路径,然后判断它是不是以/testRouteLocator/开头。
    * */
    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
    
    
        return builder.routes()
                .route(p -> p
                        .predicate(exchange -> exchange.getRequest().getPath().subPath(0).toString().startsWith(("/testRouteLocator/")))
                        .filters(f -> f.rewritePath("/testRouteLocator/(?<remaining>.*)", "/${remaining}"))
                        .uri("lb://gulimall-product"))
                .route(p -> p
                        .predicate(exchang->exchang.getRequest().getPath().toString().equals("/routelocator"))
                        .uri("lb://gulimall-product"))
                .build();
    }
}

要約する

これはゲートウェイ、knife4j、swagger を単純に統合しただけですが、多くのルーティング ルール (PrefixStrip)、ルーティングの記述 (yml と java)、レスポンシブ プログラミングなどが含まれます。このプロセスは私にとって非常に困難です。
なぜrouteLocatorとgatewaPropertiesの交差がルートの一致に使用されるのかはまだ少し不明です。元のブロガーの意図がわかりません。これを行わなければならないシナリオが思いつきません(gatewaPropertiesだけができると思います) SwaggerResource.Task の取得を完了するために使用されます)、彼がこのように書いたので、最初はこのように使用し、後で答えを調べます。

おすすめ

転載: blog.csdn.net/weixin_45654405/article/details/126513991