繁体   English   中英

使用默认接口方法作为 controller 动作 ASP.NET 核心

[英]Use default interface method as controller action ASP.NET Core

如何使用默认接口方法作为 MVC controller 动作? 由于接口方法存在于接口类型上,默认情况下它们不会被 ASP 作为操作发现。 例子:

public interface IGetEntityControllerMixin<TEntity> : IControllerBase
    where TEntity : class, IEntity, new()
{
    IRepository<TEntity> Repository { get; }

    [HttpGet("{id:int}")]
    public async Task<ActionResult<TEntity>> Get(int id)
    {
        var entity = await Repository.GetByIdAsync(id);

        return entity == null ? NotFound() : Ok(entity);
    }
}
public interface IPagingEntityControllerMixin<TEntity> : IControllerBase
    where TEntity : class, IEntity, new()
{ ... }

[ApiController]
[Route("[controller]")]
public class MyEntityController : ControllerBase,
                                  IGetEntityControllerMixin<MyEntity>,
                                  IPagingEntityControllerMixin<MyEntity>
{
    public IRepository<MyEntity> Repository { get; }
    public MyEntityController(IRepository<MyEntity> repository)
        => Repository = repository;
}

您可以注册IApplicationModelProvider以通知 MVC 这些操作。

internal sealed class ControllerDefaultInterfaceMethodActionDescriptorProvider : IApplicationModelProvider
{
    private readonly IServiceProvider _serviceProvider;

    public ControllerDefaultInterfaceMethodActionDescriptorProvider(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;

    public void OnProvidersExecuting(ApplicationModelProviderContext context) { }

    // based on https://github.com/dotnet/aspnetcore/blob/d3b7623a90d79719c0efe5fa0098f698176efa16/src/Mvc/Mvc.Core/src/ApplicationModels/DefaultApplicationModelProvider.cs#L42
    public void OnProvidersExecuted(ApplicationModelProviderContext context)
    {
        const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
        var type = Type.GetType("Microsoft.AspNetCore.Mvc.ApplicationModels.DefaultApplicationModelProvider, Microsoft.AspNetCore.Mvc.Core");
        Debug.Assert(type != null);
        object defaultModelProvider = _serviceProvider.GetServices<IApplicationModelProvider>()
                                                      .First(x => x.GetType() == type);

        // internal ActionModel? CreateActionModel(TypeInfo typeInfo, MethodInfo methodInfo)
        Type createActionType = typeof(Func<,,,>).MakeGenericType(type, typeof(TypeInfo), typeof(MethodInfo), typeof(ActionModel));
        var createActionModelDelegate = Delegate.CreateDelegate(createActionType, type.GetMethod("CreateActionModel", bindingFlags)!);
        // internal ParameterModel CreateParameterModel(ParameterInfo parameterInfo)
        Type createParameterModelType = typeof(Func<,,>).MakeGenericType(type, typeof(ParameterInfo), typeof(ParameterModel));
        var createParameterModelDelegate = Delegate.CreateDelegate(createParameterModelType, type.GetMethod("CreateParameterModel", bindingFlags)!);

        var createActionParams = new object [3];
        createActionParams[0] = defaultModelProvider;
        var createParameterParams = new object [2];
        createParameterParams[0] = defaultModelProvider;

        foreach (ControllerModel controllerModel in context.Result.Controllers)
        {
            var controllerType = controllerModel.ControllerType;
            createActionParams[1] = controllerType;

            foreach (Type @interface in controllerType.ImplementedInterfaces)
            {
                var mapping = controllerType.GetInterfaceMap(@interface);
                for (var i = 0; i < mapping.InterfaceMethods.Length; ++i)
                {
                    MethodInfo interfaceMethod = mapping.InterfaceMethods[i];
                    MethodInfo targetMethod = mapping.TargetMethods[i];

                    // check is method implemented by interface itself
                    if (targetMethod != interfaceMethod)
                        continue;

                    createActionParams[2] = interfaceMethod;
                    var actionModel = (ActionModel?)createActionModelDelegate.DynamicInvoke(createActionParams);
                    if (actionModel == null)
                        continue;

                    actionModel.Controller = controllerModel;
                    controllerModel.Actions.Add(actionModel);

                    foreach (var parameterInfo in actionModel.ActionMethod.GetParameters())
                    {
                        createParameterParams[1] = parameterInfo;
                        var parameterModel = (ParameterModel?)createParameterModelDelegate.DynamicInvoke(createParameterParams);
                        if (parameterModel != null)
                        {
                            parameterModel.Action = actionModel;
                            actionModel.Parameters.Add(parameterModel);
                        }
                    }
                }
            }
        }
    }

    public int Order => 1;
}

暂无
暂无

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

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