单独库

时间:2017-02-21 10:27:28

标签: c# asp.net-core asp.net-core-mvc

我在一个单独的类库中开发MVC API。 API方法使用属性路由。 API将由其他MVC应用程序使用(不是由我构建的)。

主MVC应用程序将引用我的库程序集并在其自己的启动类中调用AddMvc() / UseMvc()。它可以动态地为我的API库设置根API URL(来自配置或选项设置委托),这样就可以确保它与自己的路由没有冲突,可以使用属性路由或集中路由。

因此,我们假设我的API库有product/{id}路由。主应用程序应该能够选择任何路由前缀,例如api/product/{id}some/other/prefix/product/{id}

在启动时,MVC将发现所有引用程序集中的所有控制器/路由,它还将发现并注册我的API库路由,但仅限于没有任何前缀的硬编码product/{id}路由。

我一直试图让MVC注册带有前缀的路由,但到目前为止还没有成功。主应用程序将调用自定义AddMyApi() / UseMyApi()配置方法,因此我可以为我的库进行配置/设置。我试过的一些事情:

映射

app.Map("/custom-prefix", api =>
{
    api.UseMvc();
});

这会导致custom-prefix/product/{id}product/{id}的重复路由。

路线惯例

基于http://www.strathweb.com/2016/06/global-route-prefix-with-asp-net-core-mvc-revisited/

services.AddMvc(options =>
{
    options.Conventions.Insert(0, new RouteConvention(new RouteAttribute("custom-prefix")));
});

看起来这不起作用,因为主要应用程序调用{​​{1}}会覆盖这些选项,反之亦然,这取决于首先调用哪些选项。

自定义路线属性

基于Controller类上的AddMvc()的自定义路由属性将无法工作,因为我需要从options类注入前缀,并且属性不支持构造函数注入。

推迟发现路线

基于http://www.strathweb.com/2015/04/asp-net-mvc-6-discovers-controllers/

我已将IRouteTemplateProvider添加到库控制器,以防止它们在主应用程序的启动时被发现。但是我以后无法添加它们,而且我想我会遇到主应用程序再次覆盖MVC选项的相同问题。

领域

我无法使用区域,因为主应用程序可能决定从根运行API(不带前缀)。

所以我不知道如何解决这个问题。任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:7)

我认为一个约定是正确的方法,你缺少的只是为你的库提供适当的扩展方法在MVC中注册。

首先创建一个约定,该约定将为通过某个选择器的所有控制器添加前缀。

  • 它基于one I wrote添加文化前缀,但这个想法与您链接的文章非常相似。
  • 基本上,它会更新任何现有的AttributeRouteModel,或者如果找不到则添加新的。{/ li>

这将是这种惯例的一个例子:

public class ApiPrefixConvention: IApplicationModelConvention
{
    private readonly string prefix;
    private readonly Func<ControllerModel, bool> controllerSelector;
    private readonly AttributeRouteModel onlyPrefixRoute;
    private readonly AttributeRouteModel fullRoute;

    public ApiPrefixConvention(string prefix, Func<ControllerModel, bool> controllerSelector)
    {
        this.prefix = prefix;
        this.controllerSelector = controllerSelector;            

        // Prepare AttributeRouteModel local instances, ready to be added to the controllers

        //  This one is meant to be combined with existing route attributes
        onlyPrefixRoute = new AttributeRouteModel(new RouteAttribute(prefix));

        //  This one is meant to be added as the route for api controllers that do not specify any route attribute
        fullRoute = new AttributeRouteModel(
            new RouteAttribute("api/[controller]"));
    }

    public void Apply(ApplicationModel application)
    {
        // Loop through any controller matching our selector
        foreach (var controller in application.Controllers.Where(controllerSelector))
        {
            // Either update existing route attributes or add a new one
            if (controller.Selectors.Any(x => x.AttributeRouteModel != null))
            {
                AddPrefixesToExistingRoutes(controller);
            }
            else
            {
                AddNewRoute(controller);
            }
        }
    }        

    private void AddPrefixesToExistingRoutes(ControllerModel controller)
    {
        foreach (var selectorModel in controller.Selectors.Where(x => x.AttributeRouteModel != null).ToList())
        {
            // Merge existing route models with the api prefix
            var originalAttributeRoute = selectorModel.AttributeRouteModel;                
            selectorModel.AttributeRouteModel =
                AttributeRouteModel.CombineAttributeRouteModel(onlyPrefixRoute, originalAttributeRoute);
        }
    }

    private void AddNewRoute(ControllerModel controller)
    {
        // The controller has no route attributes, lets add a default api convention 
        var defaultSelector = controller.Selectors.First(s => s.AttributeRouteModel == null);
        defaultSelector.AttributeRouteModel = fullRoute;
    }
} 

现在,如果这是您正在编写的应用程序的一部分而不是库,您只需将其注册为:

services.AddMvc(opts =>
{
    var prefixConvention = new ApiPrefixConvention("api/", (c) => c.ControllerType.Namespace == "WebApplication2.Controllers.Api");
    opts.Conventions.Insert(0, prefixConvention);
});

但是,由于您提供了一个库,因此您需要提供一个类似AddMyLibrary("some/prefix")的扩展方法,该方法将负责添加此约定以及任何其他设置,例如注册所需的服务。

因此,您可以为IMvcBuilder编写扩展方法,并更新该方法中的MvcOptions。好消息是,因为它是IMvcBuilder的扩展名,所以它将始终在默认AddMvc()之后调用:

public static IMvcBuilder AddMyLibrary(this IMvcBuilder builder, string prefix = "api/")
{
    // instantiate the convention with the right selector for your library.
    // Check for namespace, marker attribute, name pattern, whatever your prefer
    var prefixConvention = new ApiPrefixConvention(prefix, (c) => c.ControllerType.Namespace == "WebApplication2.Controllers.Api");

    // Insert the convention within the MVC options
    builder.Services.Configure<MvcOptions>(opts => opts.Conventions.Insert(0, prefixConvention));

    // perform any extra setup required by your library, like registering services

    // return builder so it can be chained
    return builder;
}

然后,您会要求您的图书馆用户将其包含在他们的应用中,如下所示:

services.AddMvc().AddMyLibrary("my/api/prefix/");

答案 1 :(得分:-1)

//尝试使用此参考文献enter link description here

 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
            app.UsePathBase("/Api/v/00");

     app.Map("/api/v/0", api =>
        {
            api.UseMvc();
        });        
 }