如何构建一个自定义模型绑定器,它将根据请求上下文返回不同类型的模型?

发布于 2024-12-18 06:19:35 字数 1392 浏览 2 评论 0原文

我收到了针对特定操作的传入请求(来自 Facebook 的积分处理),该操作将具有不同的内容,因此我有不同的模型类来处理该请求。

这是我的动作:

public ActionResult Index([ModelBinder(typeof(FacebookCreditModelBinder))] IFacebookRequest facebookRequest)
{
    if (facebookRequest is FacebookPaymentsGetItemsRequest)
    {
        // do whatever
    }
}

这是我的模型活页夹。

public class FacebookCreditModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var binder = new DefaultModelBinder();
        // how to change the model here in the bindingContext?
        return binder.BindModel(controllerContext, bindingContext); 
    }
}

例如,如果传入的 var“method”是“ payments_get_items”,我想创建一个 FacebookPaymentsGetItemsRequest 对象,如果方法是“ payments_status_update”,我想创建一个 FacebookPaymentsStatusUpdateRequest 对象,我不知道如何更改绑定上下文中模型的类型。 是否可以更改自定义模型绑定器中模型的类型?


其他方法:我也用 BindModel 尝试过,我能够返回正确的对象,但所有属性均为 null,因为它不是由默认模型绑定器填充的:

public override object BindModel(ControllerContext controllerContext,
        ModelBindingContext bindingContext)
{
    NameValueCollection form = controllerContext.HttpContext.Request.Form;
    if (form.Get("method") == "payments_get_items")
    {
        return new FacebookPaymentsGetItemsRequest();
    }
    ...

I have incoming requests (from Facebook for Credits handling) on a specific action which will have have different contents, so I have different model classes to handle that.

This is my action:

public ActionResult Index([ModelBinder(typeof(FacebookCreditModelBinder))] IFacebookRequest facebookRequest)
{
    if (facebookRequest is FacebookPaymentsGetItemsRequest)
    {
        // do whatever
    }
}

And this is my model binder.

public class FacebookCreditModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var binder = new DefaultModelBinder();
        // how to change the model here in the bindingContext?
        return binder.BindModel(controllerContext, bindingContext); 
    }
}

I want to create for example a FacebookPaymentsGetItemsRequest object if the incoming var "method" is "payments_get_items" and a FacebookPaymentsStatusUpdateRequest if method is "payments_status_update" and I don't know how to change the type of the model in the bindingContext.
Is it possible to change the type of the model in a custom model binder?


Other approach: I tried it with BindModel also and I'm able to return the correct object but all properties are null because it is not filled by the default model binder:

public override object BindModel(ControllerContext controllerContext,
        ModelBindingContext bindingContext)
{
    NameValueCollection form = controllerContext.HttpContext.Request.Form;
    if (form.Get("method") == "payments_get_items")
    {
        return new FacebookPaymentsGetItemsRequest();
    }
    ...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

信愁 2024-12-25 06:19:35

您可以这样做:

public class FacebookCreditModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var methodValue = bindingContext.ValueProvider.GetValue("method");
        if (methodValue == null || string.IsNullOrEmpty(methodValue.AttemptedValue))
        {
            throw new Exception("The method parameter was not found");
        }

        var method = methodValue.AttemptedValue;
        IFacebookRequest model = null;
        if (method == "payments_get_items")
        {
            model = FacebookPaymentsGetItemsRequest();
        }
        else if (method == "...")
        {
            model = ....
        }
        else
        {
            throw new NotImplementedException("Unknown method value: " + method);
        }

        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());
        return model;
    }
}

并在 Application_Start 中注册:

ModelBinders.Binders.Add(typeof(IFacebookRequest), new FacebookCreditModelBinder());

然后您的控制器操作可能如下所示:

public ActionResult Index(IFacebookRequest facebookRequest)
{
    if (facebookRequest is FacebookPaymentsGetItemsRequest)
    {
        // do whatever
    }
}

You could do this:

public class FacebookCreditModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var methodValue = bindingContext.ValueProvider.GetValue("method");
        if (methodValue == null || string.IsNullOrEmpty(methodValue.AttemptedValue))
        {
            throw new Exception("The method parameter was not found");
        }

        var method = methodValue.AttemptedValue;
        IFacebookRequest model = null;
        if (method == "payments_get_items")
        {
            model = FacebookPaymentsGetItemsRequest();
        }
        else if (method == "...")
        {
            model = ....
        }
        else
        {
            throw new NotImplementedException("Unknown method value: " + method);
        }

        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());
        return model;
    }
}

and register in Application_Start:

ModelBinders.Binders.Add(typeof(IFacebookRequest), new FacebookCreditModelBinder());

Then your controller action could look like this:

public ActionResult Index(IFacebookRequest facebookRequest)
{
    if (facebookRequest is FacebookPaymentsGetItemsRequest)
    {
        // do whatever
    }
}
聊慰 2024-12-25 06:19:35

.NET Core 中似乎不存在 DefaultModelBinder。网络上的其他解决方案使用过时的类型ComplexTypeModelBinder。其中一些人没有填写非共享字段。我花了几个小时尝试一些东西。这是我的简单解决方案。

public class FacebookCreditModelBinder : IModelBinder
{
    private readonly ModelBinderProviderContext _providerCtx;

    // pass the ModelBinderProviderContext when 
    // creating this inside your IModelBinderProvider
    public FacebookCreditModelBinder(ModelBinderProviderContext providerCtx)
    {
        _providerCtx = providerCtx;
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
            throw new ArgumentNullException(nameof(bindingContext));

        // do some code to choose type
        var chosenType = typeof(FacebookPaymentsGetItemsRequest);

        // then do this
        var metadata = _providerCtx.MetadataProvider.GetMetadataForType(chosenType);
        var binder = _providerCtx.CreateBinder(metadata, _providerCtx.BindingInfo);
        bindingContext.ModelMetadata = metadata;
        return binder.BindModelAsync(bindingContext);
    }
}

DefaultModelBinder seems absent in .NET Core. Other solutions on the web use the obsolete type ComplexTypeModelBinder. Some of them were not filling out not-shared fields. I spent several hours trying out stuff. So here's my simple solution.

public class FacebookCreditModelBinder : IModelBinder
{
    private readonly ModelBinderProviderContext _providerCtx;

    // pass the ModelBinderProviderContext when 
    // creating this inside your IModelBinderProvider
    public FacebookCreditModelBinder(ModelBinderProviderContext providerCtx)
    {
        _providerCtx = providerCtx;
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
            throw new ArgumentNullException(nameof(bindingContext));

        // do some code to choose type
        var chosenType = typeof(FacebookPaymentsGetItemsRequest);

        // then do this
        var metadata = _providerCtx.MetadataProvider.GetMetadataForType(chosenType);
        var binder = _providerCtx.CreateBinder(metadata, _providerCtx.BindingInfo);
        bindingContext.ModelMetadata = metadata;
        return binder.BindModelAsync(bindingContext);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文