【问题标题】:.NET Core Middleware - access IApplicationBuilder in a controller?.NET Core 中间件 - 在控制器中访问 IApplicationBuilder?
【发布时间】:2021-05-12 20:59:17
【问题描述】:

我需要访问控制器内的 IApplicationBuilder。

我尝试过的:-

我已经编写了中间件(app.UseMyMiddleware)如下

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IHttpContextAccessor httpContextAccessor)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseCookiePolicy();
        app.UseMyMiddleware();

        app.UseAuthentication();
        app.UseSession();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
public class MyMiddleware
{
    private readonly RequestDelegate _next;

    public MyMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        ///TODO - Pass IApplicationBuilder to HttpContext 
        await _next(context);
    }
}
public static class MiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

但我不知道如何在 Invoke 方法中将 IApplicationBuilder 传递给 HttpContext。所以,我可以在控制器中使用它。 我还提到了以下stackoverflow问答

  1. how to access IApplicationBuilder in a controller?
  2. .Net Core Middleware - Getting Form Data from Request

问题:-

  1. Invoke 方法中如何将 IApplicationBuilder 传递给 HttpContext 以在控制器中使用它?

  2. 除了中间件,还有什么更好的方法可以访问控制器内部的 IApplicationBuilder 吗?

【问题讨论】:

  • IApplicationBuilder 旨在在启动时构建应用程序,运行一次,而不是稍后在运行时运行。所以我认为它不能为您提供任何 runtime 服务或 api,您想要什么?这比如何提供更重要。
  • @KingKing 我想使用这个 IApplicationBuilder 在控制器中配置 Hangfire。您可以参考问题stackoverflow.com/questions/66022553/…了解更多详情
  • 您以后无法在任何地方访问IApplicationBuilder,但您可以在运行时使用UseWhen 插入中间件。但是过滤条件不能利用你的MVC model binding,因为你只有HttpContext作为输入,你自己从原始源(查询字符串、路由数据、标题、表单、请求正文)中提取请求数据。您可以以某种方式实现一个设置,记住或发送每个请求的选定选项)。
  • @KingKing 你能给我举个例子我怎么能得到这个?
  • @KingKing 谢谢!为了您的努力,我会检查并通知您。

标签: asp.net-core httpcontext


【解决方案1】:

IApplicationBuilder 并非按照您希望的方式工作。相反,如果您在构建时创建了一些希望可用于中间件的数据,请向服务添加单例并将单例注入到中间件中。

【讨论】:

  • 感谢您的信息。有没有其他解决方法来解决这个问题?我已经尝试过以下答案中提供的 KingKing 方法,但使用该方法我无法根据“builder.UseWhen”条件更改“app.UseHangfireDashboard”配置。(仅在应用程序启动时配置)。
  • 查看您打算如何在控制器中使用 IApplicationBuilder 的示例会很有帮助。如果你告诉我,我也许可以为你提供一个解决方法。
  • @Developer 我很确定您不知道如何应用我的解决方案,.UseWhen 是您可以尝试的唯一选择。但看起来你不需要解决它,好吧。许多用户通过来回响应来获得最终解决方案,但您只是保持沉默,因此其他人没有机会提供帮助。这只是你的选择。
  • @KingKing 非常抱歉延迟回复。我已经使用了您的解决方案,但使用它我无法根据条件(使用 .UseWhen)更改hangfire 配置(表示hangfire 连接字符串)。现在,我已经添加了我的答案。感谢您的努力,很抱歉延迟回复。
【解决方案2】:

在完成应用程序构建阶段(运行Configure 方法之后)之后,您将无法访问IApplicationBuilder。它根本不能用于注射。 但是为了在运行时根据请求数据(来自HttpContext)插入或配置中间件,您可以使用.UseWhen。终端中间件的另一个是.MapWhen,但我认为这不适合你的情况。以下是.UseWhen 的示例:

public static class MiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        var allOptions = new [] {"option 1","option 2"};
        foreach(var option in allOptions){
            var currentOption = option;
            builder.UseWhen(context => {
              //suppose you can get the user's selected option from query string
              var selectedOption = context.Request.Query["option_key"];
              return selectedOption == currentOption;
            }, app => {
               //your MyMiddleware is supposed to accept one argument
               app.UseMiddleware<MyMiddleware>(currentOption);
            });
        }
        return builder;
    }
}

为了简化,我想您的选项只是字符串,您必须事先知道用户可以通过 UI 选择的所有可能选项。每一个都与插入中间件的条件完全匹配,并且必须都是独占的(所以只要其中一个就可以启用一个对应的中间件),否则会出现重复的中间件,这可能会导致一些问题。

通过将上面的foreach表达得更清楚,它可能表示如下:

//kind of pseudo code
if(selectedOption1){
    app.UseMiddleware<MyMiddleware>("option 1");
} else if(selectedOption2){
    app.UseMiddleware<MyMiddleware>("option 2");
}
...

您必须决定如何从用户那里获得选定的选项(在上面的示例中,我从query string 获得)。您也可以从Cookie 获取它(以记住用户的选择)或从其他来源(例如路由数据、标头、表单、请求正文)获取。我认为这是另一个问题,所以如果您对此有任何疑问,请在另一个问题中提问。

【讨论】:

    【解决方案3】:

    首先感谢@Kingking 和@GlennSills 提供的解决方案和宝贵的cmets。

    我已经解决了这个问题

    创建了一个继承自Hangfire.JobStorage的类,如下

    public class HangfireSqlServerStorageExtension : Hangfire.JobStorage
    {
        private readonly HangfireSqlServerStorage _hangfireSqlServerStorage = new HangfireSqlServerStorage();
    
        public HangfireSqlServerStorageExtension(string nameOrConnectionString)
        {
            _hangfireSqlServerStorage.SqlServerStorageOptions = new SqlServerStorageOptions();
            _hangfireSqlServerStorage.SqlServerStorage = new SqlServerStorage(nameOrConnectionString, _hangfireSqlServerStorage.SqlServerStorageOptions);
        }
        public HangfireSqlServerStorageExtension(string nameOrConnectionString, SqlServerStorageOptions options)
        {
            _hangfireSqlServerStorage.SqlServerStorageOptions = options;
            _hangfireSqlServerStorage.SqlServerStorage = new SqlServerStorage(nameOrConnectionString, _hangfireSqlServerStorage.SqlServerStorageOptions);
        }
    
        public void UpdateConnectionString(string nameOrConnectionString)
        {
            _hangfireSqlServerStorage.SqlServerStorage = new SqlServerStorage(nameOrConnectionString, _hangfireSqlServerStorage.SqlServerStorageOptions);
        }
    
        public override IStorageConnection GetConnection()
        {
            return _hangfireSqlServerStorage.SqlServerStorage.GetConnection();
        }
    
        public override IMonitoringApi GetMonitoringApi()
        {
            return _hangfireSqlServerStorage.SqlServerStorage.GetMonitoringApi();
        }
    }
    

    HangfireSqlServerStorage.cs

    在上面的HangfireSqlServerStorageExtension类中使用

    public class HangfireSqlServerStorage
    {
        public SqlServerStorage SqlServerStorage { get; set; }
        public SqlServerStorageOptions SqlServerStorageOptions { get; set; }
    }
    

    Startup.cs

    在启动文件中为 HangfireSqlServerStorageExtension 实例添加单例服务,并如下配置hangfire仪表板

    public class Startup
    {
        ///Other necessary code here
        
        public static HangfireSqlServerStorageExtension HangfireSqlServerStorageExtension { get; private set; }
    
        public void ConfigureServices(IServiceCollection services)
        {
           ///Other necessary code here
           
            HangfireSqlServerStorageExtension = new HangfireSqlServerStorageExtension("DBConnecttionString"));
            services.AddSingleton<HangfireSqlServerStorageExtension>(HangfireSqlServerStorageExtension);
            services.AddHangfire(configuration => configuration.SetDataCompatibilityLevel(CompatibilityLevel.Version_170));
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, IHttpContextAccessor httpContextAccessor)
        {
           //Other necessary code here
    
            app.UseHangfireDashboard("/Dashboard", new DashboardOptions(), HangfireSqlServerStorageExtension);
    
            //Other necessary code here
        }
    }
    

    在控制器内部我使用它如下

    HangfireController.cs

    public class HangfireController : Controller
    {
       protected readonly HangfireSqlServerStorageExtension 
       hangfireSqlServerStorageExtension;
    
    public HangfireController(HangfireSqlServerStorageExtension hangfireSqlServerStorageExtension)
    {
        this.hangfireSqlServerStorageExtension = hangfireSqlServerStorageExtension;
    }
    
    public IActionResult DisplayHangfireDashboard()
    {
        // Update connString as follows
        hangfireSqlServerStorageExtension.UpdateConnectionString(connString);
        var hangfireDashboardUrl = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}" + "/Dashboard";
        return Json(new { url = hangfireDashboardUrl });
    }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-08-23
      • 2017-03-19
      • 1970-01-01
      • 1970-01-01
      • 2017-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多