【问题标题】:Bind from custom headers and request body从自定义标头和请求正文绑定
【发布时间】:2013-12-10 07:03:42
【问题描述】:

我在同时从自定义标头和请求正文绑定模型时遇到问题。

这是我的模型:

public class TestModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public HeaderModel HeaderModel { get; set; }
}
public class HeaderModel
{
    public int Version { get; set; }
    public string Test { get; set; }
}

我有一个简单的测试页面,其中发布了以下内容:

$(function() {
    var go = function() {
        $.ajax({
            type: 'POST',
            url: '/api/values',
            data: { id: 1, name: 'theValue2' },
            headers: { HeaderModel: JSON.stringify({version: 1, test:"roar"}) }
        }).always();
    };
    $('input').click(go);
});

我创建了以下标头值提供程序:

public class HeaderValueProvider<T> : IValueProvider where T : class
{
    private readonly HttpRequestHeaders _headers;

    public HeaderValueProvider(HttpRequestHeaders headers)
    {
        _headers = headers;
    }

    public bool ContainsPrefix(string prefix)
    {
        var test = typeof (T).Name == prefix;
        return test;
    }

    public ValueProviderResult GetValue(string key)
    {
        if (typeof (T).Name == key)
        {
            IEnumerable<string> headerStrings;
            _headers.TryGetValues(key, out headerStrings);
            var strings = headerStrings.ToArray();
            if (headerStrings != null && strings.Any())
            {
                var value = strings.First();
                var obj = JsonConvert.DeserializeObject<T>(value,
                    new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore});
                return new ValueProviderResult(obj, value, CultureInfo.InvariantCulture);
            }
        }
        return null;
    }
}

public class HeaderValueProviderFactory<T> : ValueProviderFactory where T : class
{
    public override IValueProvider GetValueProvider(HttpActionContext actionContext)
    {
        var headers = actionContext.ControllerContext.Request.Headers;
        return new HeaderValueProvider<T>(headers);
    }
}

并且它已经在配置中注册了:

config.Services.Add(typeof(ValueProviderFactory), new HeaderValueProviderFactory<HeaderModel>());

如果我像下面这样使用我的控制器,我会绑定我的模型,但不是我的标题。它使用media formatter

public IHttpActionResult Post(TestModel model)
{
    return Ok(model);
}

如果我包含 ModelBinder 属性,我会得到 HeaderModel 绑定,但不是来自请求正文的模型的其余部分:

public IHttpActionResult Post([ModelBinder]TestModel model)
{
    return Ok(model);
}

让两者都工作的最干净的方法是什么?

【问题讨论】:

    标签: c# asp.net-web-api http-headers model-binding value-provider


    【解决方案1】:

    默认情况下,Web API 使用媒体格式化程序从 body 绑定复杂类型,例如 TestModel。通过指定 ModelBinder,您要求 Web API 使用模型绑定,它适用于 URI、查询字符串,但由于您有一个值提供者,它也是从标头绑定的。但基本上,除非您编写自定义参数绑定器,否则您将无法从正文和其他地方(如标题、查询字符串等)创建 TestModel 模型。以this 为例。

    【讨论】:

    • 感谢您抽出宝贵时间查看此内容。你说的是对的。我确实找到了一个非常简单的方法来解决这个问题。查看标记的答案。
    【解决方案2】:

    这很容易解决。正如 Badri 正确指出的那样,您不能将请求正文和标头用于相同的绑定。但是,您可以有两个参数并分别绑定它们。

    我已将 HeaderModel 保留为自己的单独类,并为其添加了 ModelBinder 属性。

    public class TestModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    
    [ModelBinder]
    public class HeaderModel
    {
        public int Version { get; set; }
        public string Test { get; set; }
    }
    

    我的帖子中现在有两个参数。

    public IHttpActionResult Post(HeaderModel headerModel, TestModel model)
    {
        return Ok();
    }
    

    我不认为这是一个完美的解决方案,但效果很好。

    【讨论】:

      猜你喜欢
      • 2023-03-21
      • 2015-05-09
      • 2014-08-08
      • 2016-06-24
      • 2013-11-13
      • 2014-08-13
      • 1970-01-01
      • 2012-05-17
      • 2017-09-28
      相关资源
      最近更新 更多