绑定自定义标头和请求正文

时间:2013-12-10 07:03:43

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

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

这是我的模特:

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);
}

让两者兼得的最简洁方法是什么?

2 个答案:

答案 0 :(得分:3)

Web API默认使用媒体格式化程序从正文绑定复杂类型,如TestModel。通过指定ModelBinder,您要求Web API使用模型绑定,该模型绑定适用于URI,查询字符串,但由于您具有值提供程序,因此它也是从标头绑定的。但基本上,除非您编写自定义参数绑定器,否则您将无法从主体和其他位置(如标题,查询字符串等)创建TestModel模型。请查看this以获取示例。

答案 1 :(得分: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; }
}

我现在在Post中有两个参数。

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

我不认为这是一个完美的解决方案,但它运作良好。