Car*_*lis 8 c# asp.net-core-mvc asp.net-core
我正在一个单独的类库中开发一个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注册带有前缀的路由,但到目前为止还没有成功.主应用程序将调用custom AddMyApi()/ UseMyApi()config方法,因此我可以为我的库进行配置/设置.我试过的一些事情:
制图
app.Map("/custom-prefix", api =>
{
api.UseMvc();
});
Run Code Online (Sandbox Code Playgroud)
这将导致两个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")));
});
Run Code Online (Sandbox Code Playgroud)
看起来这样做不会起作用,因为主应用程序调用AddMvc()或其他方式会覆盖选项,具体取决于首先调用哪些选项.
自定义路由属性
基于IRouteTemplateProviderController类的自定义路由属性将不起作用,因为我需要从options类注入前缀,并且属性不支持构造函数注入.
推迟发现路线
基于http://www.strathweb.com/2015/04/asp-net-mvc-6-discovers-controllers/
我已经添加[NonController]到库控制器中,以防止它们在主应用程序的启动时被发现.但是我以后无法添加它们,而且我想我会遇到主应用程序再次覆盖MVC选项的相同问题.
地区
我不能使用区域,因为主应用程序可能决定从根运行API(没有前缀).
所以我不知道如何解决这个问题.任何帮助表示赞赏.
我相信一个约定是正确的方法,你缺少的是为你的库提供适当的扩展方法在MVC中注册.
首先创建一个约定,该约定将为通过某个选择器的所有控制器添加前缀.
这将是这样一个惯例的一个例子:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,如果这是您正在编写的应用程序的所有部分而不是库,您只需将其注册为:
services.AddMvc(opts =>
{
var prefixConvention = new ApiPrefixConvention("api/", (c) => c.ControllerType.Namespace == "WebApplication2.Controllers.Api");
opts.Conventions.Insert(0, prefixConvention);
});
Run Code Online (Sandbox Code Playgroud)
但是,由于您提供了一个库,您想要的是提供类似的扩展方法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;
}
Run Code Online (Sandbox Code Playgroud)
然后,您会要求您的图书馆用户将其包含在他们的应用中,如下所示:
services.AddMvc().AddMyLibrary("my/api/prefix/");
Run Code Online (Sandbox Code Playgroud)