Préfixe de routage dynamic pour les contrôleurs dans une bibliothèque séparée

Je développe une API MVC dans une bibliothèque de classes séparée. Les méthodes de l’API utilisent le routage d’atsortingbut. L’API sera utilisé par d’autres applications MVC (non créées par moi).

L’application MVC principale référence l’assemblage de ma bibliothèque et appelle AddMvc() / UseMvc() dans sa propre classe de démarrage. Il sera en mesure de définir de manière dynamic l’URL de l’API racine pour ma bibliothèque d’API (à partir du délégué de configuration ou d’options), afin de garantir l’absence de conflits avec ses propres itinéraires, qui peuvent utiliser le routage par atsortingbut ou centralisé.

Donc, disons que ma bibliothèque d’API a une route product/{id} . L’application principale doit pouvoir choisir n’importe quel préfixe de route, comme api/product/{id} ou some/other/prefix/product/{id} .

Au démarrage, MVC détecte tous les contrôleurs / itinéraires dans tous les assemblages référencés. Il détecte et enregistre également les itinéraires de ma bibliothèque d’API, mais uniquement sur l’itinéraire product/{id} codé en dur sans préfixe.

J’ai essayé de faire en sorte que MVC enregistre les routes avec un préfixe, mais jusqu’à présent, sans succès. L’application principale appelle des méthodes de configuration personnalisées AddMyApi() / UseMyApi() afin que je puisse effectuer la configuration de ma bibliothèque. Certaines des choses que j’ai essayées:

Cartographie

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

Cela entraînera des itinéraires en double pour custom-prefix/product/{id} et product/{id} .

Route Convention

Basé sur 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 RouteAtsortingbute("custom-prefix"))); }); 

Il semble que cela ne fonctionnera pas car les options seront écrasées par l’appel de AddMvc() de l’application principale, ou AddMvc() , selon l’appelé en premier.

Atsortingbut d’itinéraire personnalisé

Un atsortingbut d’itinéraire personnalisé basé sur IRouteTemplateProvider sur les classes de contrôleur ne fonctionnera pas car j’ai besoin du préfixe injecté à partir d’une classe d’options, et les atsortingbuts ne prennent pas en charge l’injection de constructeur.

Reporter la découverte des itinéraires

Basé sur http://www.strathweb.com/2015/04/asp-net-mvc-6-discovers-controllers/

J’ai ajouté [NonController] aux contrôleurs de bibliothèque pour empêcher leur découverte au démarrage de l’application principale. Cependant, je n’ai pas été en mesure de les append plus tard, et je suppose que je rencontrerai le même problème de l’application principale qui écrase à nouveau les options de MVC.

Zones

Je ne peux pas utiliser de zones, car l’application principale peut décider d’exécuter l’API à partir de la racine (sans préfixe).

Je suis donc coincé sur la façon de résoudre ce problème. Toute aide est appréciée.

Je crois qu’une convention est la bonne approche ici et le peu qui vous manque est simplement de fournir la méthode d’extension appropriée pour que votre bibliothèque soit enregistrée dans MVC.

Commencez par créer une convention qui appenda un préfixe à tous les contrôleurs qui passent un certain sélecteur.

  • Il est basé sur celui que j’ai écrit pour append des préfixes de culture, mais l’idée est très similaire à celle de l’article que vous avez lié.
  • Fondamentalement, il mettra à jour tout AtsortingbuteRouteModel existant ou en appenda un nouveau s’il n’en trouve aucun.

Ce serait un exemple d’une telle convention:

 public class ApiPrefixConvention: IApplicationModelConvention { private readonly ssortingng prefix; private readonly Func controllerSelector; private readonly AtsortingbuteRouteModel onlyPrefixRoute; private readonly AtsortingbuteRouteModel fullRoute; public ApiPrefixConvention(ssortingng prefix, Func controllerSelector) { this.prefix = prefix; this.controllerSelector = controllerSelector; // Prepare AtsortingbuteRouteModel local instances, ready to be added to the controllers // This one is meant to be combined with existing route atsortingbutes onlyPrefixRoute = new AtsortingbuteRouteModel(new RouteAtsortingbute(prefix)); // This one is meant to be added as the route for api controllers that do not specify any route atsortingbute fullRoute = new AtsortingbuteRouteModel( new RouteAtsortingbute("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 atsortingbutes or add a new one if (controller.Selectors.Any(x => x.AtsortingbuteRouteModel != null)) { AddPrefixesToExistingRoutes(controller); } else { AddNewRoute(controller); } } } private void AddPrefixesToExistingRoutes(ControllerModel controller) { foreach (var selectorModel in controller.Selectors.Where(x => x.AtsortingbuteRouteModel != null).ToList()) { // Merge existing route models with the api prefix var originalAtsortingbuteRoute = selectorModel.AtsortingbuteRouteModel; selectorModel.AtsortingbuteRouteModel = AtsortingbuteRouteModel.CombineAtsortingbuteRouteModel(onlyPrefixRoute, originalAtsortingbuteRoute); } } private void AddNewRoute(ControllerModel controller) { // The controller has no route atsortingbutes, lets add a default api convention var defaultSelector = controller.Selectors.First(s => s.AtsortingbuteRouteModel == null); defaultSelector.AtsortingbuteRouteModel = fullRoute; } } 

Maintenant, si tout cela faisait partie d’une application que vous écrivez au lieu d’une bibliothèque, il vous suffirait de l’enregistrer en tant que:

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

Cependant, puisque vous fournissez une bibliothèque, vous souhaitez fournir une méthode d’extension telle que AddMyLibrary("some/prefix") qui se chargera de l’ajout de cette convention et de toute autre configuration telle que l’enregistrement des services requirejs.

Vous pouvez donc écrire une méthode d’extension pour IMvcBuilder et mettre à jour les MvcOptions intérieur de cette méthode. La bonne chose est que, puisqu’il s’agit d’une extension de IMvcBuilder , il sera toujours appelé après le AddMvc() par défaut AddMvc() :

 public static IMvcBuilder AddMyLibrary(this IMvcBuilder builder, ssortingng prefix = "api/") { // instantiate the convention with the right selector for your library. // Check for namespace, marker atsortingbute, 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(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; } 

Ensuite, vous demanderiez aux utilisateurs de votre bibliothèque de l’inclure dans leur application, comme dans:

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