Prefisso del percorso dinamico per i controller in una libreria separata

Prefisso del percorso dinamico per i controller in una libreria separata

Credo che una convenzione sia l'approccio giusto qui e il bit che ti manca è solo fornire il metodo di estensione appropriato per la registrazione della tua libreria all'interno di MVC.

Inizia creando una convenzione che aggiungerà un prefisso a tutti i controller che passano un determinato selettore.

  • Si basa su uno che ho scritto per l'aggiunta di prefissi di cultura, ma l'idea è molto simile all'articolo che hai linkato.
  • Fondamentalmente aggiornerà qualsiasi AttributeRouteModel esistente oppure aggiungine uno nuovo se non ne trovi uno.

Questo sarebbe un esempio di tale convenzione:

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

Ora, se facesse tutto parte di un'app che stai scrivendo invece di una libreria, la registreresti semplicemente come:

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

Tuttavia, poiché stai fornendo una libreria, ciò che desideri è fornire un metodo di estensione come AddMyLibrary("some/prefix") che si occuperà di aggiungere questa convenzione e qualsiasi altra configurazione come la registrazione dei servizi richiesti.

Quindi puoi scrivere un metodo di estensione per IMvcBuilder e aggiorna il MvcOptions dentro quel metodo. La cosa bella è che since è un'estensione di IMvcBuilder , verrà sempre chiamato dopo il AddMvc() predefinito :

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

Quindi chiederesti agli utenti della tua libreria di includerla nella loro app come in:

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