简体   繁体   English

如何为多个 ASP.NET MVC 控制器应用公共路由

[英]How to apply a common route for multiple ASP.NET MVC controllers

I have a common base controller and a couple of inherited controller classes with similar routes applied to them.我有一个共同的基础 controller 和几个继承的 controller 类,它们应用了类似的路由。 I want to apply the common part of the different routes and apply it to the base class.我想应用不同路线的公共部分并将其应用到基础 class。 Below is the bare minimum of my code:以下是我的代码的最低限度:

using Microsoft.AspNetCore.Mvc;

public abstract class CommonController : Controller
{
    // Some common code here
}

[Route("api/v1/BookLookup")]
public class BooksController : CommonController
{
    public async Task<JsonResult> GetAsync([FromBody]Dto1 filterParameters)
    {
        return await GetApiData(filterParameters);
    }
}

[Route("api/v1/MovieLookup")]
public class MoviesController : CommonController
{
    public async Task<JsonResult> GetAsync([FromBody]Dto1 filterParameters)
    {
        return await GetApiData(filterParameters);
    }
}

I want to have something like the following:我想要以下内容:

using Microsoft.AspNetCore.Mvc;

[Route("api/v1/")] // or any other attribute?
public abstract class CommonController : Controller
{
    // Some common code here
}

[Route("BookLookup")]
public class BooksController : CommonController
{
    public async Task<JsonResult> GetAsync([FromBody]Dto1 filterParameters)
    {
        return await GetApiData(filterParameters);
    }
}

[Route("MovieLookup")]
public class MoviesController : CommonController
{
    public async Task<JsonResult> GetAsync([FromBody]Dto1 filterParameters)
    {
        return await GetApiData(filterParameters);
    }
}

is this possible and if it is, how?这可能吗?如果是,怎么做?

The project targets .NET Core 2.0 and the Controller class in the above example is ASP.NET Core MVC controller class, not Web API controller. The project targets .NET Core 2.0 and the Controller class in the above example is ASP.NET Core MVC controller class, not Web API controller.

The built-in RouteAttribute will not work with controller inheritance by default.默认情况下,内置RouteAttribute不适用于 controller inheritance。 However, to achieve your goal, you could create a custom IControllerModelConvention to apply a rule that takes consideration of controller inheritance at startup-time.但是,为了实现您的目标,您可以创建自定义IControllerModelConvention以应用在启动时考虑 controller inheritance 的规则。

To avoid mixing up with the default [RouteAttribute] , I create a custom MyRoutePrefixAttribute instead of using the default [RouteAttribute] (Feel free to use the [RouteAttribute] if you think this behavior should be overrided ):为了避免与默认的[RouteAttribute] ,我创建了一个自定义MyRoutePrefixAttribute而不是使用默认的[RouteAttribute] (如果您认为应该覆盖此行为,请随意使用[RouteAttribute] ):

[AttributeUsage(AttributeTargets.Class)]
public class MyRoutePrefixAttribute : Attribute
{
    public MyRoutePrefixAttribute(string prefix) { Prefix = prefix; }
    public string Prefix { get; }
}

And to lookup this [MyRoutePrefixAttribute] from the inheritance chain, I create a RoutePrefixConvention that will combine the prefixes recursively:为了从 inheritance 链中查找这个[MyRoutePrefixAttribute] ,我创建了一个RoutePrefixConvention递归组合前缀:

public class RoutePrefixConvention : IControllerModelConvention
{
    public void Apply(ControllerModel controller)
    {
        foreach (var selector in controller.Selectors)
        {
            var prefixes = GetPrefixes(controller.ControllerType);  // [prefix, parentPrefix, grandpaPrefix,...]
            if(prefixes.Count ==  0) continue;
            // combine these prefixes one by one
            var prefixRouteModels = prefixes.Select(p => new AttributeRouteModel(new RouteAttribute(p.Prefix)))
                .Aggregate( (acc , prefix)=> AttributeRouteModel.CombineAttributeRouteModel(prefix, acc));
            selector.AttributeRouteModel =  selector.AttributeRouteModel != null ?
                AttributeRouteModel.CombineAttributeRouteModel(prefixRouteModels, selector.AttributeRouteModel):
                selector.AttributeRouteModel = prefixRouteModels;
        }
    }

    private IList<MyRoutePrefixAttribute> GetPrefixes(Type controlerType)
    {
        var list = new List<MyRoutePrefixAttribute>();
        FindPrefixesRec(controlerType, ref list);
        list = list.Where(r => r!=null).ToList();
        return list;

        // find [MyRoutePrefixAttribute('...')] recursively 
        void FindPrefixesRec(Type type, ref List<MyRoutePrefixAttribute> results)
        {
            var prefix = type.GetCustomAttributes(false).OfType<MyRoutePrefixAttribute>().FirstOrDefault();
            results.Add(prefix);   // null is valid because it will seek prefix from parent recursively
            var parentType = type.BaseType;
            if(parentType == null) return;
            FindPrefixesRec(parentType, ref results);
        }
    }
}

this convention will NOT influence the performance: it only searches all the [MyRoutePrefixAttribute] attributes through the inheritance chain at startup time.此约定不会影响性能:它仅在启动时通过 inheritance 链搜索所有[MyRoutePrefixAttribute]属性。

Finally, don't forget to add this convention within your startup:最后,不要忘记在你的启动中添加这个约定:

services.AddMvc(opts =>
{
     opts.Conventions.Add(new RoutePrefixConvention());
});

Controller: Controller:

[MyRoutePrefix("api/v1")]
public class CommonController : Controller
{ 
}

[Microsoft.AspNetCore.Mvc.Route("MovieLookup")]
public class MoviesController : CommonController
{
    public string GetAsync()
    {
        return "MovieLookup";
    }
}

Test Result:测试结果:

在此处输入图像描述

Hope this can help you.希望这可以帮到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM