5

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

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

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

'Name' 和 'Category' 都是字符串,'Price' 是 Decimal 而 'Stock' 是 Integer。

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

  • 补丁,http : //playapi.azurewebsites.net/api/products/1{"Price": 600.00}
  • 补丁,http : //playapi.azurewebsites.net/api/products/1 {"Category": "Electronics"}

但是,如果你发送这个,它会返回 200/OK,但不会进行更新并且库存保持在原始值

  • 补丁,http : //playapi.azurewebsites.net/api/products/1 {"Stock": 4}

我的控制器代码是相当标准的样板代码(来自脚手架的 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; }
    }
}

当我调试控制器时,我看到“补丁”对象有一个_changedProperties集合,当我执行整数请求时,其中没有项目,但是当我执行任何其他类型的请求时,它具有我更改的键。

Web API 是否应该支持对整数属性的 PATCH 请求?如果是这样,我是否需要在服务器或客户端上做任何特别的事情才能使其工作?

4

2 回答 2

5

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

public Int64 Stock { get; set; }

据我了解,用于修补现有对象的 Delta 对象不使用 JSON.net 进行转换,并且在解析 JSON 然后与数据库中的现有对象进行比较时默默地抛出 Invalid cast 异常。您可以在此处阅读有关该错误的更多信息:http: //aspnetwebstack.codeplex.com/workitem/777

于 2015-03-12T12:00:21.247 回答
0

如果您实际上无法成功更改数据类型,则可能有一个不错的 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);
            }
        }

    }
}

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

于 2017-01-19T18:33:36.393 回答