扩展 AspNetCore 视图发现

Extend AspNetCore View Discovery

我正在寻找一种方法来扩展 AspNetCore MVC 视图发现逻辑。我希望能够从控制器继承并让新控制器能够访问基本控制器的操作。有没有一种方法可以扩展视图发现逻辑,以便您可以告诉控制器在哪里寻找它的视图,在控制器的文件夹中查找,根据基本控制器的名称在文件夹中查找,甚至在基于控制器命名空间的文件夹?

~/Controllers/UserAccountController.cs

namespace App.Controllers.UserAccount 
{
    public class UserAccountController {
       public virtual async Task<IActionResult> Action1()
       {
          return View();
       }
    }
}

~/Controllers/UserAccountExtController.cs

namespace App.Controllers.UserAccount 
{
    public class UserAccountExtController : UserAccountController {
       public override async Task<IActionResult> Action1()
       {
          return View();
       }
    }
}

有没有一种方法可以扩展视图发现逻辑,如果它在视图文件夹中找不到与控制器名称同名的视图,它将根据控制器的属性,还是继承控制器的文件夹,控制器所在的文件夹,还是基于控制器命名空间的文件夹?

由于 RandyBuchholz 关于将 ActionContext 转换为 ControllerActionContext 的提示,我最终使用 IViewLocationExpander 来解决问题,这使我能够识别控制器的 BaseType。如果控制器的默认位置不存在视图,这允许添加检查 BaseController 的默认位置的约定。

public class MyViewLocationExpander : IViewLocationExpander
{
    public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
    {
        // list used for future extension
        var alternateLocations = new List<string>();

        if (context.ActionContext.ActionDescriptor is ControllerActionDescriptor descriptor)
        {
            var baseType = descriptor.ControllerTypeInfo.BaseType.Name;
            if (!baseType.StartsWith("Controller"))
            {
                var baseLocation = baseType.Replace("Controller", string.Empty);
                alternateLocations.Add("/Views/" + baseLocation + "/{0}.cshtml");
            }
        }

        var locations = viewLocations.ToList();
        locations.InsertRange(locations.IndexOf("/Views/Shared/{0}.cshtml") - 1, alternateLocations);

        return locations;
    }

    public void PopulateValues(ViewLocationExpanderContext context)
    {

    }
}

然后只需在 Startup.cs

中注册 IViewLocationExpander
    public void ConfigureServices(IServiceCollection services)
    {
        //...
        services.Configure<RazorViewEngineOptions>(options =>
            {
                var expander = new MyViewLocationExpander();
                options.ViewLocationExpanders.Add(expander);
            });
       //...
    }