gpt4 book ai didi

c# - 单独库中 Controller 的动态路由前缀

转载 作者:太空狗 更新时间:2023-10-29 20:19:35 25 4
gpt4 key购买 nike

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

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

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

在启动时,MVC 会发现所有引用程序集中的所有 Controller /路由,它还会发现并注册我的 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")));
});

看起来这行不通,因为选项将被主应用程序对 AddMvc() 的调用覆盖,反之亦然,具体取决于哪个先被调用。

自定义路由属性

基于 Controller 类上的 IRouteTemplateProvider 的自定义路由属性将不起作用,因为我需要从选项类注入(inject)的前缀,并且属性不支持构造函数注入(inject)。

延迟路由发现

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

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

区域

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

所以我对如何解决这个问题感到困惑。感谢您的帮助。

最佳答案

我认为这里的约定是正确的方法,而您缺少的一点只是为要在 MVC 中注册的库提供适当的扩展方法。

首先创建一个约定,为所有通过特定选择器的 Controller 添加一个前缀。

  • 它基于 one I wrote用于添加文化前缀,但这个想法与您链接的文章非常相似。
  • 基本上,它会更新任何现有的 AttributeRouteModel 或添加一个新的(如果未找到)。

这将是此类约定的示例:

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/");

关于c# - 单独库中 Controller 的动态路由前缀,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42364745/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com