Web API 2不处理整数的PATCH请求

时间:2015-03-12 11:13:27

标签: asp.net rest asp.net-web-api integer http-patch

我遇到了Web API 2(.net 4.5.1)的问题,因为它似乎忽略了属性为整数的PATCH请求,但是处理其他类型没有问题(我测试了字符串和小数) )。

我在http://playapi.azurewebsites.net/api/products设置了一个带有“产品”控制器的不安全测试API。如果您对该网址进行GET,您将获得类似此产品的内容:

{"Id": 1,"Name": "Xbox One","Category": "gaming","Price": 300,"Stock": 5}

'名称'和'类别'都是字符串,'Price'是十进制,'Stock'是整数。

如果您发送这些请求,它们都可以正常工作(您将获得200 / OK的更新实体):

但是,如果您发送此信息,则返回200 / OK,但不进行更新,并且库存保持原始值

我的控制器代码是相当标准的锅炉板代码(来自脚手架ODATA控制器,但移入标准API控制器):

// PATCH: api/Products/5
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> PatchOrder(int id, Delta<Product> patch)
{
    Validate(patch.GetEntity());
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    var item = await db.Products.FindAsync(id);
    if (item == null)
    {
        return NotFound();
    }
    patch.Patch(item);
    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!ProductExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }
    return Ok(item);
}

我的'产品'模型如下:

namespace PlayAPI.Models
{
    public class Product
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public double Price { get; set; }
        public int Stock { get; set; }
    }
}

当我调试控制器时,我发现'patch'对象有一个_changedProperties集合,当我执行整数请求时它没有任何项目,但当我做任何其他类型的请求时,它有我改变的关键。

Web API是否应支持整数属性的PATCH请求?如果是这样,我是否需要在服务器或客户端上执行任何特殊操作才能使其正常工作?

2 个答案:

答案 0 :(得分:4)

作为快速解决方法,在PlayAPI.Models.Product上将int更改为Int64。

public Int64 Stock { get; set; }

我的理解是,用于修补现有对象的Delta对象不使用JSON.net进行转换,并在分析JSON时静默抛出无效转换异常,然后与数据库中的现有对象进行比较。您可以在此处详细了解该错误:http://aspnetwebstack.codeplex.com/workitem/777

答案 1 :(得分:0)

如果您无法成功更改数据类型,可能会有一个可以使用的正常黑客修复程序。只是将不可读的数据附加到查询字符串中。

这是您可以在Patch功能中调用的功能。只要你没有使用专门命名它的查询字符串参数,你应该没问题。

/// <summary>
/// Tries to attach additional parameters from the query string onto the delta object. 
/// This uses the parameters extraInt32 and extraInt16, which can be used multiple times.
/// The parameter format is "PropertyName|Integer"
/// <para>Example: ?extraInt32=Prop1|123&extraInt16=Prop2|88&extraInt32=Prop3|null</para>
/// </summary>
[NonAction]
protected void SetAdditionalPatchIntegers<TEntity>(Delta<TEntity> deltaEntity, bool allowNull = true)
{
    var queryParameters = Request.GetQueryNameValuePairs();
    foreach (var param in queryParameters.Where(pair => 
                                pair.Key == "extraInt32" || 
                                pair.Key == "extraInt16"))
    {
        if (param.Value.Count(v => v == '|') != 1)
            continue;
        var splitParam = param.Value.Split('|');

        if (allowNull && 
                (String.IsNullOrWhiteSpace(splitParam[1]) || 
                splitParam[1].Equals("null", StringComparison.OrdinalIgnoreCase)))
        {
            deltaEntity.TrySetPropertyValue(splitParam[0], null);
            continue;
        }

        if (param.Key == "extraInt32")
        {
            int extraInt;
            if (Int32.TryParse(splitParam[1], out extraInt))
            {
                deltaEntity.TrySetPropertyValue(splitParam[0], extraInt);
            }
        }
        if (param.Key == "extraInt16")
        {
            short extraShort;
            if (Int16.TryParse(splitParam[1], out extraShort))
            {
                deltaEntity.TrySetPropertyValue(splitParam[0], extraShort);
            }
        }

    }
}

我真的很讨厌没有更好的答案,但至少可以采取一些措施。