zoukankan      html  css  js  c++  java
  • Ocelot自定义管道中间件

    Ocelot是啥就不介绍了哈,网关大家都知道,如果大家看过源码会发现其核心就是由一个个的管道中间件组成的,当然这也是Net Core的亮点之一。一个请求到来,会按照中间件的注册顺序进行处理,今天的问题出在Ocelot管道中间件这里,现在有个需求是想要在网关层面对数据进行加密解密,前端会对数据进行加密,通过网关的时候进行解密传递下游,下游返回数据网关进行加密返回给前端。

    所以这里就想在走Ocelot管道前后注册两个中间件对请求和结果数据进行处理。然后就按想的去做,但悲催的是,Ocelot处理完后就直接返回数据给调用方了,根本没有走它后面的管道中间件,查资料才知道Ocelot之后不会再调用下一个管道中间件了,这就蛋疼了,怎么办??

    突然想到Ocelot应该会提供扩展来让使用者自定义管道中间件吧,回答当然是可以的,本篇我们就来自定义一个管道中间件放在Ocelot中。

    首先需要写一个自己的中间件,需要继承OcelotMiddleware:

    public class TestResponseMiddleware: OcelotMiddleware
        {
            private readonly OcelotRequestDelegate _next;
            public TestResponseMiddleware(OcelotRequestDelegate next,IOcelotLoggerFactory loggerFactory)
                : base(loggerFactory.CreateLogger<TestResponseMiddleware>())
            {
                _next = next;
            }
    
    
            public async Task Invoke(DownstreamContext context)
            {
                if (!context.IsError && context.HttpContext.Request.Method.ToUpper() != "OPTIONS")
                {
                    //对返回结果进行加密
                    //Logger.LogInformation("");
                    if (context.HttpContext.Response != null && context.DownstreamResponse.Content.Headers.ContentLength > 0)
                    {
                        var result= await context.DownstreamResponse.Content.ReadAsStringAsync();
                        using (var md5 = MD5.Create())
                        {
                            var md5Result = md5.ComputeHash(Encoding.ASCII.GetBytes(result));
                            var strResult = BitConverter.ToString(md5Result);
                            strResult = strResult.Replace("-", "");
                            context.HttpContext.Response.Body.Write(Encoding.UTF8.GetBytes(strResult));
                        }
                    }
                }
                else
                {
                    await _next.Invoke(context);
                }
            }
        }

    这个逻辑就是拿到请求结果之后对数据进行MD5加密,然后再返回。

    然后我们新建一个注册类,创建专门用来注册管道中间件的方法:

        public static class TestServiceExtension
        {
            public static IOcelotPipelineBuilder UseTestResponseMiddleware(this IOcelotPipelineBuilder builder)
            {
                return builder.UseMiddleware<TestResponseMiddleware>();
            }
        }

    然后就是重点了!!我们需要去翻Ocelot的源码,找到其中注册管道中间件的地方,然后把那个类文件复制过来,放到自己的项目中引用,你可以理解为修改了源码来用。我们首先要看的是OcelotMiddlewareExtensions.cs文件,这里是Ocelot管道中间件的调用入口,不信你看UseOcelot扩展方法:

    public static class OcelotMiddlewareExtensions
        {
            public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder)
            {
                await builder.UseOcelot(new OcelotPipelineConfiguration());
                return builder;
            }

    我们要看的是这里面的另外一个方法,Ocelot管道的CreateOcelotPipeline:

            private static IApplicationBuilder CreateOcelotPipeline(IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
            {
                var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices);
    
                pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration);
    
                var firstDelegate = pipelineBuilder.Build();
    
                /*
                inject first delegate into first piece of asp.net middleware..maybe not like this
                then because we are updating the http context in ocelot it comes out correct for
                rest of asp.net..
                */
    
                builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";
    
                builder.Use(async (context, task) =>
                {
                    var downstreamContext = new DownstreamContext(context);
                    await firstDelegate.Invoke(downstreamContext);
                });
    
                return builder;
            }

    可以看到里面 pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration)这句代码,这是Ocelot管道中间件的创建方法,我们要修改的就是这两个地方,这个方法在OcelotPipelineExtensions.cs类文件里,点进去看一下:

     public static class OcelotPipelineExtensions
        {
            public static OcelotRequestDelegate BuildCustomeOcelotPipeline(this IOcelotPipelineBuilder builder,
                OcelotPipelineConfiguration pipelineConfiguration)
            {
                // This is registered to catch any global exceptions that are not handled
                // It also sets the Request Id if anything is set globally
                builder.UseExceptionHandlerMiddleware();
    
                // If the request is for websockets upgrade we fork into a different pipeline
                builder.MapWhen(context => context.HttpContext.WebSockets.IsWebSocketRequest,
                    app =>
                    {
                        app.UseDownstreamRouteFinderMiddleware();
                        app.UseDownstreamRequestInitialiser();
                        app.UseLoadBalancingMiddleware();
                        app.UseDownstreamUrlCreatorMiddleware();
                        app.UseWebSocketsProxyMiddleware();
                    });
    
                // Allow the user to respond with absolutely anything they want.
                builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware);
    
                // This is registered first so it can catch any errors and issue an appropriate response
                builder.UseResponderMiddleware();
    
                // Then we get the downstream route information
                builder.UseDownstreamRouteFinderMiddleware();
    
                // This security module, IP whitelist blacklist, extended security mechanism
                builder.UseSecurityMiddleware();
    
                //Expand other branch pipes
                if (pipelineConfiguration.MapWhenOcelotPipeline != null)
                {
                    foreach (var pipeline in pipelineConfiguration.MapWhenOcelotPipeline)
                    {
                        builder.MapWhen(pipeline);
                    }
                }
    
                // Now we have the ds route we can transform headers and stuff?
                builder.UseHttpHeadersTransformationMiddleware();
    
                // Initialises downstream request
                builder.UseDownstreamRequestInitialiser();
    
                // We check whether the request is ratelimit, and if there is no continue processing
                builder.UseRateLimiting();
    
                // This adds or updates the request id (initally we try and set this based on global config in the error handling middleware)
                // If anything was set at global level and we have a different setting at re route level the global stuff will be overwritten
                // This means you can get a scenario where you have a different request id from the first piece of middleware to the request id middleware.
                builder.UseRequestIdMiddleware();
    
                // Allow pre authentication logic. The idea being people might want to run something custom before what is built in.
                builder.UseIfNotNull(pipelineConfiguration.PreAuthenticationMiddleware);
    
                // Now we know where the client is going to go we can authenticate them.
                // We allow the ocelot middleware to be overriden by whatever the
                // user wants
                if (pipelineConfiguration.AuthenticationMiddleware == null)
                {
                    builder.UseAuthenticationMiddleware();
                }
                else
                {
                    builder.Use(pipelineConfiguration.AuthenticationMiddleware);
                }
    
                // The next thing we do is look at any claims transforms in case this is important for authorisation
                builder.UseClaimsToClaimsMiddleware();
    
                // Allow pre authorisation logic. The idea being people might want to run something custom before what is built in.
                builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware);
    
                // Now we have authenticated and done any claims transformation we
                // can authorise the request
                // We allow the ocelot middleware to be overriden by whatever the
                // user wants
                if (pipelineConfiguration.AuthorisationMiddleware == null)
                {
                    builder.UseAuthorisationMiddleware();
                }
                else
                {
                    builder.Use(pipelineConfiguration.AuthorisationMiddleware);
                }
    
                // Now we can run the claims to headers transformation middleware
                builder.UseClaimsToHeadersMiddleware();
    
                // Allow the user to implement their own query string manipulation logic
                builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware);
    
                // Now we can run any claims to query string transformation middleware
                builder.UseClaimsToQueryStringMiddleware();
    
                // Get the load balancer for this request
                builder.UseLoadBalancingMiddleware();
    
                // This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
                builder.UseDownstreamUrlCreatorMiddleware();
    
                // Not sure if this is the best place for this but we use the downstream url
                // as the basis for our cache key.
                builder.UseOutputCacheMiddleware();
    
                //We fire off the request and set the response on the scoped data repo
                builder.UseHttpRequesterMiddleware();
    
                //添加自定义测试中间件
             builder.UseTestResponseMiddleware();
    
                return builder.Build();
            }
    
            private static void UseIfNotNull(this IOcelotPipelineBuilder builder,
                Func<DownstreamContext, Func<Task>, Task> middleware)
            {
                if (middleware != null)
                {
                    builder.Use(middleware);
                }
            }
        }

    我们可以看到其实就是一个个的扩展方法的调用,也是我们所说的中间件,我在最后把刚才写的中间件加了上去,就是标红的那里,现在所有进入Ocelot的请求就都会走我们的中间件处理了,使用的时候需要将修改的OcelotPipelineExtensions.cs类文件放在自己项目里哦。

    大功告成!

  • 相关阅读:
    BZOJ1233 干草堆
    POJ1321棋盘问题【搜索】
    1008
    10.2训练赛
    2014 ACM/ICPC Asia Regional Shanghai Online【未完成】
    hdu5045||2014 ACM/ICPC Asia Regional Shanghai Online【数位dp】
    0926
    poj1007【求逆序数】
    hlg1287数字去重和排序II【hash】
    hlgChocolate Auction【并查集】
  • 原文地址:https://www.cnblogs.com/weiBlog/p/11241535.html
Copyright © 2011-2022 走看看