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

Web API 2 does not process PATCH requests for Integers

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

我在 http://playapi.azurewebsites.net/api/products 使用 'products' 控制器设置了一个不安全的测试 API。如果你对那个 URL 执行 GET,你会得到类似这个产品的东西:

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

‘Name’和‘Category’都是字符串,‘Price’是小数,‘Stock’是整数。

如果您发送这些请求,它们都会起作用(您将获得更新实体的 200/OK):

然而,如果你发送这个,它 returns 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);
}

我的 'Product' 模型如下:

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 请求吗?如果是这样,我是否需要在服务器或客户端上执行任何特殊操作才能使其正常工作?

作为快速修复,将 PlayAPI.Models.Product 上的 int 更改为 Int64。

public Int64 Stock { get; set; }

据我了解,用于修补现有对象的 Delta 对象不使用 JSON.net 进行转换,而是在解析 JSON 然后与数据库中的现有对象。您可以在此处阅读有关此错误的更多信息:http://aspnetwebstack.codeplex.com/workitem/777

如果您实际上无法成功更改数据类型,您可能可以使用一个不错的 hack 修复程序。只是将不可读的数据附加到查询字符串中。

这是一个您可以从 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);
            }
        }

    }
}

我真的很讨厌没有更好的答案,但至少可以做些什么。