如何在odata控制器中实现与常规PUT相同的POST以更新实体

时间:2019-06-03 18:29:14

标签: asp.net-web-api2 odata

考虑到ODataController的PUT method的以下典型实现,我如何使完全相同的方法也可以作为POST使用?

我正在开发一个OData端点,该端点将从无法控制的外部系统调用。似乎该系统通过发送带有uri键而不是使用PUT的POST错误地实现了Update语义(以告诉我的系统更新实体)。

public async Task<IHttpActionResult> Put([FromODataUri] int key, Product update)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    if (key != update.Id)
    {
        return BadRequest();
    }
    db.Entry(update).State = EntityState.Modified;
    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }
    return Updated(update);
}

我的第一个猜测是用[AcceptVerbs(“ PUT”,“ POST”)]注释该方法,以使相同的确切方法实现可用作POST,但这是行不通的。可能是ODataConventionModelBuilder默认设置对此一无所知...

理想情况下,我想保留基于标准的PUT和用于插入的常规POST,但要添加一个与put相同但仅在动词上有所不同的特殊帖子。

谢谢

2 个答案:

答案 0 :(得分:1)

在关于外部数据源/外部对象的odata端点实现中找到一些不太明显的documentation on salesforce.com之后,对我来说很明显,salesforce.com尝试在外部对象上调用POST进行更新语义,但是还添加了X-HTTP-METHOD设置为PATCH。

因此,解决方案是实现以下类:

   public class MethodOverrideHandler : DelegatingHandler
    {
        readonly string[] _methods = { "DELETE", "HEAD", "PUT", "PATCH", "MERGE" };
        const string _header1 = "X-HTTP-Method-Override";
        const string _header2 = "X-HTTP-Method";//salesforce special behavior???

        protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
            // Check for HTTP POST with the X-HTTP-Method-Override header.
            if (request.Method == HttpMethod.Post && request.Headers.Contains(_header1))
            {
                // Check if the header value is in our methods list.
                var method = request.Headers.GetValues(_header1).FirstOrDefault();
                if (_methods.Contains(method, StringComparer.InvariantCultureIgnoreCase))
                {
                    // Change the request method.
                    request.Method = new HttpMethod(method);
                }
            }
            else  if (request.Method == HttpMethod.Post && request.Headers.Contains(_header2))
            {
                // Check if the header value is in our methods list.
                var method = request.Headers.GetValues(_header2).FirstOrDefault();
                if (_methods.Contains(method, StringComparer.InvariantCultureIgnoreCase))
                {
                    // Change the request method.
                    request.Method = new HttpMethod(method);
                }
            }
            return base.SendAsync(request, cancellationToken);
        }
    }

并按如下方式在WebApiConfig.Register(HttpConfiguration config)中注册:

config.MessageHandlers.Add(new MethodOverrideHandler());

现在,用于外部对象上Salesforce更新操作的不符合odata的POST将委派给我最初发布的PUT方法的符合标准的odata实施(在ODataController中)。

我希望这对以后的人有帮助...

答案 1 :(得分:0)

我的方法是使用while (y < 768 )将更多的逻辑放入该方法中,以检查并查看数据库中是否已存在记录,然后检查数据是否为空。

update.Id

EDIT 刚刚注意到ProductExists方法...我会将其从catch块中取出并投入尝试

public async Task<IHttpActionResult> Put([FromODataUri] int key, Product update)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    //might have to get rid of this condition for the sake of new entries
    //if (key != update.Id)
    //{
        //return BadRequest();
    //}

    try
    {
        //not sure what the name of your table is so I'm going to call it ProductTable
        var foo = db.ProductTable.Where(p => p.Id == update.Id).FirstOrDefault();
        if(foo == null)
        {
            db.Entry(update).State = EntityState.Added;
            await db.SaveChangesAsync();
            return StatusCode(HttpStatusCode.Accepted);
        }
        else
        {
            db.Entry(update).State = EntityState.Modified;
            await db.SaveChangesAsync();
            return Updated(update);
        }
    }
    catch (DbUpdateConcurrencyException ex)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
       {
            throw new DbUpdateConcurrencyException(ex.Message);
       }
    }
}