繁体   English   中英

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

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

我有一个共同的基础 controller 和几个继承的 controller 类,它们应用了类似的路由。 我想应用不同路线的公共部分并将其应用到基础 class。 以下是我的代码的最低限度:

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

我想要以下内容:

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

这可能吗?如果是,怎么做?

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.

默认情况下,内置RouteAttribute不适用于 controller inheritance。 但是,为了实现您的目标,您可以创建自定义IControllerModelConvention以应用在启动时考虑 controller inheritance 的规则。

为了避免与默认的[RouteAttribute] ,我创建了一个自定义MyRoutePrefixAttribute而不是使用默认的[RouteAttribute] (如果您认为应该覆盖此行为,请随意使用[RouteAttribute] ):

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

为了从 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);
        }
    }
}

此约定不会影响性能:它仅在启动时通过 inheritance 链搜索所有[MyRoutePrefixAttribute]属性。

最后,不要忘记在你的启动中添加这个约定:

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

Controller:

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

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

测试结果:

在此处输入图像描述

希望这可以帮到你。

暂无
暂无

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

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