Kestrel 和 ASP.NET 核心 MVC 使用自定义基本路径

Kestrel and ASP.NET Core MVC use custom base path

如何将应用挂载到不同的基本路径?

例如,我的控制器路由是/api/keywords,但是当运行网络服务器时,我希望基本路径是/development,所以我的控制器路由是/development/api/keywords .我宁愿不必修改我的控制器。在旧的 Web API 版本中,您可以在不同的路径中安装 OWIN 应用程序,所以我希望做类似的事情。

看看这个:

public class Program
{
   public static void Main(string[] args)
   {
        var contentRoot = Directory.GetCurrentDirectory();

        var config = new ConfigurationBuilder()
           .SetBasePath(contentRoot)
           .Build();

        var hostBuilder = new WebHostBuilder()

          //Server
           .UseKestrel()

           //Content root - in this example it will be our current directory
           .UseContentRoot(contentRoot)

           //Web root - by the default it's wwwroot but here is the place where you can change it
           .UseWebRoot("wwwroot")

           //Startup
           .UseStartup<Startup>();


        var host = hostBuilder.Build();

        host.Run();
    } 
}

有两种扩展方法 - UseWebRoot() 和 UseContentRoot() - 可用于配置 Web 和内容根。

您可以查看原大文章here

首先创建一个继承自IApplicationModelConvention接口的class

public class EnvironmentRouteConvention : IApplicationModelConvention
{
    private readonly AttributeRouteModel _centralPrefix;

    public EnvironmentRouteConvention(IRouteTemplateProvider routeTemplateProvider)
    {
        _centralPrefix = new AttributeRouteModel(routeTemplateProvider);
    }

    public void Apply(ApplicationModel application)
    {
         foreach (var controller in application.Controllers)
        {
            var matchedSelectors = controller.Selectors.Where(x => x.AttributeRouteModel != null).ToList();
            if (matchedSelectors.Any())
            {
                foreach (var selectorModel in matchedSelectors)
                {
                    //This will apply only to your API controllers. You may change that depending of your needs
                    if (selectorModel.AttributeRouteModel.Template.StartsWith("api"))
                    {
                        selectorModel.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_centralPrefix, selectorModel.AttributeRouteModel);
                    }
                }
            }
        }
    }

然后创建一个 class 只是为了更容易和更清洁的使用。

public static class MvcOptionsExtensions
{
    public static void UseEnvironmentPrefix(this MvcOptions opts, IRouteTemplateProvider routeAttribute)
    {
        opts.Conventions.Insert(0, new EnvironmentRouteConvention(routeAttribute));
    }
}

现在开始使用它,首先很常见,将您的环境保存在启动 class

的 属性 中
private IHostingEnvironment _env;

public Startup(IHostingEnvironment env)
{
    _env = env;
}

然后你需要做的就是调用你的静态扩展 class

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.UseEnvironmentPrefix(new RouteAttribute(_env.EnvironmentName));
    });
}

但是还有最后一件事需要关心。无论您有什么客户端使用您的 API,您肯定不想更改您发送的所有 URL HTTP 请求。所以诀窍是创建一个中间件,它将修改您请求的 Path 以包含您的环境名称。 ()

public class EnvironmentUrlRewritingMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task Invoke(HttpContext context, IHostingEnvironment env)
    {
        var path = context.Request.Path.ToUriComponent();
        //Again this depends of your need, whether to activate this to your API controllers only or not
        if (!path.StartsWith("/" + env.EnvironmentName) && path.StartsWith("/api"))
        {
            var newPath = context.Request.Path.ToString().Insert(0, "/" + env.EnvironmentName);
            context.Request.Path = newPath;
        }
        await _next.Invoke(context);
    }
}

你的 ConfigureServices 方法在你的 Startup class 变成了

public void ConfigureServices(IServiceCollection services)
{
    app.UseMiddleware<EnvironmentUrlRewritingMiddleware>();
    services.AddMvc(options =>
    {
        options.UseEnvironmentPrefix(new RouteAttribute(_env.EnvironmentName));
    });
}

唯一的 缺点 是它不会改变您的 URL,因此如果您使用浏览器点击 API,您将不会请参阅包含您的环境的 URL。 response.Redirect 始终发送 GET 请求,即使原始请求是 POST。我还没有找到最终的解决方案来反映 URL.

的路径

有一个名为 UsePathBase 的新方法可以轻松完成此操作。 https://github.com/aspnet/HttpAbstractions/blob/bfa183747f6fb528087554c3d6ec58ef05f1c10a/src/Microsoft.AspNetCore.Http.Abstractions/Extensions/UsePathBaseExtensions.cs