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

5
我在使用Web API 2 (.net 4.5.1)时遇到了问题,它似乎会忽略属性为整数的PATCH请求,但可以正常处理其他类型(如字符串和十进制数)。我已经设置了一个未加密的测试API,其中包含一个“products”控制器,位于http://playapi.azurewebsites.net/api/products。如果您对该URL进行GET请求,您将得到类似于此产品的返回结果:
{"Id": 1,"Name": "Xbox One","Category": "gaming","Price": 300,"Stock": 5}

‘Name’和‘Category’都是字符串,‘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请求?如果是,则需要在服务器端或客户端执行任何特殊操作才能使其正常工作吗?

这个问题有一个永久性的解决方案在这里 - Rob
2个回答

5
作为一个快速解决方案,在 PlayAPI.Models.Product 上将 int 更改为 Int64。
public Int64 Stock { get; set; }

据我了解,用于修补现有对象的Delta对象不使用JSON.net进行转换,当它解析JSON并将其与来自数据库的现有对象进行比较时,会悄无声息地抛出Invalid cast异常。您可以在此处阅读有关此错误的更多信息:http://aspnetwebstack.codeplex.com/workitem/777

1
有没有其他解决方案?在我的数据库中,我有一个 INT 类型,它在 C# 代码中与 Int64 不兼容,将数据库类型更改为 BIGINT 似乎过于浪费,因为 int 只会是 1 位数,这将浪费内存。 - Max Carroll

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

    }
}

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接