Dynamisches Routenpräfix für Controller in separater Bibliothek

Dynamisches Routenpräfix für Controller in separater Bibliothek

Ich glaube, eine Konvention ist hier der richtige Ansatz, und das, was Ihnen fehlt, ist nur die Bereitstellung der richtigen Erweiterungsmethode für Ihre Bibliothek, die in MVC registriert werden soll.

Erstellen Sie zunächst eine Konvention, die allen Controllern, die einen bestimmten Selektor passieren, ein Präfix hinzufügt.

  • Es basiert auf einem, den ich zum Hinzufügen von Kulturpräfixen geschrieben habe, aber die Idee ist dem Artikel, den Sie verlinkt haben, sehr ähnlich.
  • Grundsätzlich wird entweder jeder vorhandene AttributeRouteModel aktualisiert oder fügen Sie eine neue hinzu, wenn keine gefunden wird.

Dies wäre ein Beispiel für eine solche Konvention:

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;
    }
} 

Wenn dies alles Teil einer App wäre, die Sie schreiben, anstatt einer Bibliothek, würden Sie es einfach registrieren als:

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

Da Sie jedoch eine Bibliothek bereitstellen, möchten Sie eine Erweiterungsmethode wie AddMyLibrary("some/prefix") bereitstellen das kümmert sich um das Hinzufügen dieser Konvention und alle anderen Einstellungen wie das Registrieren erforderlicher Dienste.

Sie können also eine Erweiterungsmethode für IMvcBuilder schreiben und aktualisieren Sie die MvcOptions innerhalb dieser Methode. Das Schöne ist, dass Since eine Erweiterung von IMvcBuilder ist , wird es immer nach dem Standardwert AddMvc() aufgerufen :

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;
}

Dann würden Sie Benutzer Ihrer Bibliothek bitten, sie in ihre App aufzunehmen, wie in:

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