asp net core rc2.抽象类模型绑定 [英] Asp net core rc2. Abstract class model binding

查看:9
本文介绍了asp net core rc2.抽象类模型绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 RC1 中,我使用以下代码进行抽象类或接口绑定:

In the RC1 I use the following code for abstract classes or interfaces binding:

public class MessageModelBinder : IModelBinder {

    public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) {
        if(bindingContext.ModelType == typeof(ICommand)) {
            var msgTypeResult = bindingContext.ValueProvider.GetValue("messageType");
            if(msgTypeResult == ValueProviderResult.None) {
                return ModelBindingResult.FailedAsync(bindingContext.ModelName);
            }
            var type = Assembly.GetAssembly(typeof(MessageModelBinder )).GetTypes().SingleOrDefault(t => t.FullName == msgTypeResult.FirstValue);
            if(type == null) {
                return ModelBindingResult.FailedAsync(bindingContext.ModelName);
            }
            var metadataProvider = (IModelMetadataProvider)bindingContext.OperationBindingContext.HttpContext.RequestServices.GetService(typeof(IModelMetadataProvider));
            bindingContext.ModelMetadata = metadataProvider.GetMetadataForType(type);
        }
        return ModelBindingResult.NoResultAsync;
    }
}

此绑定器仅从查询字符串中读取模型类型(messageType 参数)并覆盖元数据类型.其余的工作由标准绑定器执行,例如 BodyModelBinder.

This binder only reads model type (messageType parameter) from query string and overrides metadata type. And the rest of the work performed by standard binders such as BodyModelBinder.

在 Startup.cs 我只添加第一个活页夹:

In Startup.cs I just add first binder:

services.AddMvc().Services.Configure<MvcOptions>(options => {
    options.ModelBinders.Insert(0, new MessageModelBinder());
});

控制器:

[Route("api/[controller]")]
public class MessageController : Controller {
    [HttpPost("{messageType}")]
    public ActionResult Post(string messageType, [FromBody]ICommand message) {
    } 
}

如何在 RC2 中执行此操作?

How can I perform this in RC2?

据我了解,现在我必须使用 IModelBinderProvider.好的,我试过了.启动.cs:

As far as I understand, now I have to use IModelBinderProvider. OK, I tried. Startup.cs:

services.AddMvc().Services.Configure<MvcOptions>(options => {
    options.ModelBinderProviders.Insert(0, new MessageModelBinderProvider());
});

ModelBinderProvider:

ModelBinderProvider:

public class MessageModelBinderProvider : IModelBinderProvider {
    public IModelBinder GetBinder(ModelBinderProviderContext context) {
        if(context == null) {
            throw new ArgumentNullException(nameof(context));
        }
        return context.Metadata.ModelType == typeof(ICommand) ? new MessageModelBinder() : null;
    }
}

模型绑定器:

public class MessageModelBinder : IModelBinder {
    public Task BindModelAsync(ModelBindingContext bindingContext) {
        if(bindingContext.ModelType == typeof(ICommand)) {
            var msgTypeResult = bindingContext.ValueProvider.GetValue("messageType");
            if(msgTypeResult == ValueProviderResult.None) {
                bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
                return Task.FromResult(0);
            }
            var type = typeof(MessageModelBinder).GetTypeInfo().Assembly.GetTypes().SingleOrDefault(t => t.FullName == msgTypeResult.FirstValue);
            if(type == null) {
                bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
                return Task.FromResult(0);
            }
            var metadataProvider = (IModelMetadataProvider)bindingContext.OperationBindingContext.HttpContext.RequestServices.GetService(typeof(IModelMetadataProvider));
            bindingContext.ModelMetadata = metadataProvider.GetMetadataForType(type);
            bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, Activator.CreateInstance(type));
        }
        return Task.FromResult(0);
    }
}

但我不能指定 NoResult.如果我没有指定 bindingContext.Result,我会在控制器中得到空模型.如果我指定 bindingContext.Result,我会得到空模型而不设置模型字段.

But I cannot specify NoResult. If I do not specify bindingContext.Result, I get null model in controller. If I specify bindingContext.Result, I get empty model without setting model fields.

推荐答案

我对自定义模型绑定和抽象类有类似的要求以及 dougbu 在 github AspNet/Mvc/issues/4703为我工作.我从 RC1 升级到 ASP.NET Core 1.0,需要根据他的建议修改我的自定义模型绑定器.我已经复制 &将他的代码粘贴在下面,以防 github 问题的链接中断.阅读 github 问题中的评论,了解有关在服务器上创建请求类型的对象的代码的安全注意事项.

I had a similar requirement with custom model binding and abstract classes and the suggestions posted by dougbu on github AspNet/Mvc/issues/4703 worked for me. I upgraded from RC1 to ASP.NET Core 1.0 and needed to modify my custom model binder with his recommendations. I've copy & pasted his code below in case the link to the github issue breaks. Read the comments in the github issue for security considerations around code that creates objects of a requested type on the server.

public class MessageModelBinderProvider : IModelBinderProvider 
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.ModelType != typeof(ICommand))
        {
            return null;
        }

        var binders = new Dictionary<string, IModelBinder>();
        foreach (var type in typeof(MessageModelBinderProvider).GetTypeInfo().Assembly.GetTypes())
        {
            var typeInfo = type.GetTypeInfo();
            if (typeInfo.IsAbstract || typeInfo.IsNested)
            {
                continue;
            }

            if (!(typeInfo.IsClass && typeInfo.IsPublic))
            {
                continue;
            }

            if (!typeof(ICommand).IsAssignableFrom(type))
            {
                continue;
            }

            var metadata = context.MetadataProvider.GetMetadataForType(type);
            var binder = context.CreateBinder(metadata);
            binders.Add(type.FullName, binder);
        }

        return new MessageModelBinder(context.MetadataProvider, binders);
    }
}

MessageModelBinder

public class MessageModelBinder : IModelBinder
{
    private readonly IModelMetadataProvider _metadataProvider;
    private readonly Dictionary<string, IModelBinder> _binders;

    public MessageModelBinder(IModelMetadataProvider metadataProvider, Dictionary<string, IModelBinder> binders)
    {
        _metadataProvider = metadataProvider;
        _binders = binders;
    }

    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var messageTypeModelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, "messageType");
        var messageTypeResult = bindingContext.ValueProvider.GetValue(messageTypeModelName);
        if (messageTypeResult == ValueProviderResult.None)
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        IModelBinder binder;
        if (!_binders.TryGetValue(messageTypeResult.FirstValue, out binder))
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        // Now know the type exists in the assembly.
        var type = Type.GetType(messageTypeResult.FirstValue);
        var metadata = _metadataProvider.GetMetadataForType(type);

        ModelBindingResult result;
        using (bindingContext.EnterNestedScope(metadata, bindingContext.FieldName, bindingContext.ModelName, model: null))
        {
            await binder.BindModelAsync(bindingContext);
            result = bindingContext.Result;
        }

        bindingContext.Result = result;
    }
}

这篇关于asp net core rc2.抽象类模型绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆