asp.net-core - 如何更改asp.net core Controller 中的静态文件位置?

标签 asp.net-core .net-core

我知道我们可以在 Configure 方法中设置 FileProvider 来服务器其他位置文件:

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(Path.Combine(env.WebRootPath, "images")),
    RequestPath = "/MyImages"
});

我想知道如何更改 Controller 中的 FileProvider 文件位置,因为我想通过用户输入重置路径。

最佳答案

在您的场景中,我认为您应该预设不同的网络根路径供用户选择。为什么这有道理?因为通常根路径应该存在并且已经包含一些内容(就像wwwroot)。如果这符合您的需求,我们可以提供一个简单的解决方案,如下所示:

//inside Startup.Configure

app.UseWhen(context => {
              var wr = context.Request.Cookies["wwwroot"];
              return string.IsNullOrEmpty(wr) || wr == "wwwroot";
            }, subApp => {
              subApp.UseStaticFiles();
}).UseWhen(context => {
              var wr = context.Request.Cookies["wwwroot"];
              return wr == "YAwwwroot";
           }, subApp => {
              subApp.UseStaticFiles(new StaticFileOptions
              {
                FileProvider = new PhysicalFileProvider(Path.Combine(HostingEnvironment.ContentRootPath, "YAwwwroot"))
              });
});

上面的代码使用 UseWhen 在前提条件(事先已知)的情况下动态交换静态文件中间件。我只是举一个简单的例子来说明它是多么简单。您要使用的另一个根路径是 YAwwwroot。使用 Cookies 只是为了演示目的,您可以通过任何其他方式从 HttpContext 访问您的数据(例如:保存有关状态的单例服务或选项)事件根路径)。正如您所看到的,我们必须事先知道根路径才能相应地编写 UseWhen 代码。您还可以编写自己的扩展方法来避免重复 UseWhen (因此,如果您有许多根路径,则不必重复许多 UseWhen)。

在某些 Controller 操作中,您可以设置事件根路径(这里与上面的代码配对,我们通过cookie设置事件根路径):

public IActionResult SetStaticFileRoot(string root)
{
    root = root ?? "wwwroot";
    Response.Cookies.Append("wwwroot", root);                      
    return Ok(root);
}

现在,如果您确实希望用户设置的事件根路径是动态的(当然完全由用户控制)。我们需要为静态文件创建一个自定义中间件。然而我们不需要重新实现太多。我们可以重用StaticFileMiddleware。该默认中间件是一个单例,它捕获 StaticFileOptions 一次。这就是为什么即使您可以更新 StaticFileOptions,它也不会产生任何效果。

我们可以在这里为静态文件实现两种中间件,作用域中间件和单例中间件。

它们的工作方式相同,对于每个请求,StaticFileOptions 的最新值将传递给默认的 StaticFileMiddleware(实例化它时)。因此,您的 Controller 操作可以直接更新 StaticFileOptions 来更改静态文件的根路径。

这是限定范围的版本:

public class ScopedStaticFileMiddleware : IMiddleware
{
    readonly IHostingEnvironment _hostingEnvironment;
    readonly IOptions<StaticFileOptions> _staticFileOptions;
    readonly ILoggerFactory _loggerFactory;
    public ScopedStaticFileMiddleware(IHostingEnvironment hostingEnvironment,
        IOptions<StaticFileOptions> staticFileOptions,
        ILoggerFactory loggerFactory)
    {
        _hostingEnvironment = hostingEnvironment;
        _staticFileOptions = staticFileOptions;
        _loggerFactory = loggerFactory;
    }

    public Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        //here we create the default static file middleware
        var staticFileMiddleware = new StaticFileMiddleware(next, _hostingEnvironment, _staticFileOptions, _loggerFactory);
        return staticFileMiddleware.Invoke(context);
    }
}

注册作用域中间件:

//inside Startup.ConfigureServices
services.AddScoped<ScopedStaticFileMiddleware>();

//inside Startup.Configure (replacing app.UseStaticFiles)
app.UseMiddleware<ScopedStaticFileMiddleware>();

这是单例版本:

public class ScopedOptionsStaticFileMiddleware
{
    readonly RequestDelegate _next;
    readonly IHostingEnvironment _hostingEnvironment;        
    readonly IOptions<StaticFileOptions> _staticFileOptions;
    readonly ILoggerFactory _loggerFactory;
    public ScopedOptionsStaticFileMiddleware(RequestDelegate next,
        IHostingEnvironment hostingEnvironment, 
        IOptions<StaticFileOptions> staticFileOptions,
        ILoggerFactory loggerFactory)             
    {
        _next = next;
        _hostingEnvironment = hostingEnvironment;
        _staticFileOptions = staticFileOptions;
        _loggerFactory = loggerFactory;            
    }

    public Task Invoke(HttpContext context)
    {
        //we create the default middleware each time processing a request
        //so the latest static file options is always used
        var defaultMiddleware = new StaticFileMiddleware(_next, _hostingEnvironment, 
                                                         _staticFileOptions, _loggerFactory);
        return defaultMiddleware.Invoke(context);
    }        
}  

使用单例中间件:

//inside Startup.Configure
app.UseMiddleware<ScopedOptionsStaticFileMiddleware>();

由于对象实例化,使用单例中间件可以为您节省一些成本/资源。

最后,要更新根路径,请相应地更改 StaticFileOptionsFileProvider:

//inject IOptions<StaticFileOptions> and get its value into _staticFileOptions
//inject IWebHostEnvironment as _environment
...

public IActionResult SetStaticFileRoot(string root) {
      root = root ?? "wwwroot";     
      //create a new file provider with the new root path       
      _staticFileOptions.FileProvider = new PhysicalFileProvider(Path.Combine(_environment.ContentRootPath, root));            
      return Ok(root);
}

注意:上面的代码不负责检查绝对根路径是否存在或是否应该创建。这是你的责任来处理它。

关于asp.net-core - 如何更改asp.net core Controller 中的静态文件位置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66758084/

相关文章:

c# - 如何编写 EF Core 查询以过滤多个表?

asp.net - 无效操作异常 : Could not find 'UserSecretsIdAttribute' on assembly

c# - .Net 5 - WPF,单元测试问题

c# - .Net Core 2.2 Web API 在 GET 上获取 415 不支持的媒体类型?

c# - 无法使用直线在 Webchat 中发送附件,相同的代码在模拟器中运行良好

xml - ASP.NET Core 的 XML 配置提供程序中的数组

validation - asp .net core - 自定义验证错误未显示在 View 中

asp.net-core - 在 ASP.NET vNext 过滤器中获取注入(inject)对象

c# - 如何在 .NET Standard 2.0/Core 2.1 中使用 PrivateKey 创建 X509Certificate2?

c# - AWS Lambda 环境变量和依赖注入(inject)