Blazor WebAssembly 集成Ocelot网关顺带解决跨域问题

Blazor WebAssembly集成Ocelot网关顺带解决跨域问题

我们知道跨域请求通常会被浏览器所拦截。这是浏览器提供的一种安全机制,即不允许一个域名下的脚本去请求另一个域名的数据。

请注意此处使用的是Blazor WebAssembly 的ASP.NET Core hosted模式,即模板具有Client,Server,Share三个项目。

问题描述

在blazor应用开发中我便遇到了这样的问题,我的项目需要用到六七个不同的站点接口,有统一登录站点,订单接口站点,门店接口站点,三方渠道站点等等。而我希望通过HttpClientFactory来获取HttpClient进行请求。

众所周知, IHttpClientFactory有多种使用方式:

  • 基本用法
  • 命名客户端
  • 类型化客户端
  • 生成的客户端

假如使用命名客户端方法,我们可以很轻松的根据名称区分开具有不同BaseAddress的HttpClient,但是公司已有一个对原生HttpClient封装的类库,因此,我必须采用类型化客户端的方法。

在此给出一个示例(包含IOC容器中HttpMessageHandler的添加):

 public static class ServiceClientExtension
    {
        public static WebAssemblyHostBuilder AddServiceClient(this WebAssemblyHostBuilder builder)
        {
            builder.Services.AddScoped<PlatformRequestHandler>();
            builder.Services
                .AddHttpClient<IServiceClient, ServiceClients.ServiceClient>((sp, client) =>
                {
                    var option = sp.GetService<ModuleOption>();
                    if (string.IsNullOrWhiteSpace(option.BaseAddress))
                    {
                        option.BaseAddress = builder.HostEnvironment.BaseAddress;
                    }
                    client.BaseAddress = new Uri(option.BaseAddress);
                })
                .AddHttpMessageHandler();

            return builder;
        }

        private static IHttpClientBuilder AddHttpMessageHandler(this IHttpClientBuilder builder)
        {
            builder.ConfigureHttpMessageHandlerBuilder(cfg =>
            {
                var handlers = cfg.Services.GetServices<DelegatingHandler>();
                foreach (var delegatingHandler in handlers)
                {
                    cfg.AdditionalHandlers.Add(delegatingHandler);
                }
            });
            return builder;
        }
    }

因此,我需要有一个请求转发站点,统一帮助我将请求转发至不同的站点去。自然而然的想到了Ocelot。Ocelot是一个优秀的开源网关项目。

我的Blazor项目中的Server,主要有以下作用:

  • 集成Ocelot,作为网关对请求进行转发
  • 集成Azure ApplicationInsights,对接口请求异常进行记录
  • 调起Blazor.Client程序

长话短说

这里我不再赘述Ocelot的应用,仅仅介绍其在Blazor中的使用。

集成Ocelot示例:

public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext, builder) =>
                {
                    builder
                        .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
                        .AddJsonFile($"/Ocelot/Ocelot.json", true, true)
                        .AddJsonFile($"/Ocelot/ocelot.{hostingContext.HostingEnvironment.EnvironmentName.ToLower()}.json", true, true)
                        .AddEnvironmentVariables();
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

客户端调用时,将HttpClientFactory的BaseAddress指定为builder.HostEnvironment.BaseAddress(见上述示例),通过在请求url中添加转发节点标识,如 /login,然后Ocelot根据你的配置文件,找到/login对应的站点,进行请求转发:

var request = new HttpRequestMessage(HttpMethod.Get, "/login/Auth/UserInfo");

怎样让Ocelot不阻塞中间件管道,当找不到转发路由路径时,进入下一个中间件

需要注意的是,Ocelot通常要求阻塞中间件管道,因此,会导致blazor页面导航失效,即正常的页面跳转也会被当成请求转发。因此,我们需要对Ocelot中间件进行改造,当其找不到转发路由时,自动进入下一个中间件。

示例如下:

        public static IApplicationBuilder UseOcelotWhenRouteMatch(this IApplicationBuilder app)
        {
            app.MapWhen(context =>
            {
                var internalConfigurationResponse = context.RequestServices.GetRequiredService<IInternalConfigurationRepository>().Get();
                if (internalConfigurationResponse.IsError || internalConfigurationResponse.Data.Routes.Count == 0)
                {
                    return false;
                }

                var internalConfiguration = internalConfigurationResponse.Data;
                var downstreamRouteFinder = context.RequestServices.GetRequiredService<IDownstreamRouteProviderFactory>().Get(internalConfiguration);

                var response = downstreamRouteFinder.Get(context.Request.Path, context.Request.QueryString.ToString(),
                    context.Request.Method, internalConfiguration, context.Request.Host.ToString());
                return !response.IsError && !string.IsNullOrEmpty(response.Data?.Route?.DownstreamRoute?.FirstOrDefault()?.DownstreamScheme);
            }, appBuilder => appBuilder.UseOcelot().Wait());

            return app;
        }

Startup.cs

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseWebAssemblyDebugging();
            }

            app.UseBlazorFrameworkFiles();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseOcelotWhenRouteMatch();

            app.UseEndpoints(builder =>
            {
                builder.MapFallbackToFile("index.html");
            });
        }

猜你喜欢

转载自blog.csdn.net/qq_40404477/article/details/109347733
今日推荐