Как реализовать такой же POST, как обычный PUT в контроллере одат, чтобы обновить сущность - PullRequest
1 голос
/ 03 июня 2019

Учитывая следующую типичную реализацию метода ODataController PUT , как я могу сделать такой же метод ТАКЖЕ доступным как POST?

Я разрабатываю конечную точку OData, котораябудет вызван из внешней системы, которую я не могу контролировать.Похоже, что эта система неправильно реализует семантику обновления (чтобы сообщить моей системе обновить сущность), отправив POST с ключом uri вместо PUT.

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 для вставок, но добавить специальный пост, который идентиченположить, но отличается только глаголом.

Спасибо

Ответы [ 2 ]

1 голос
/ 05 июня 2019

После нахождения не столь очевидной документации на 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());

Теперь POST, не соответствующий odata, для операций обновления salesforce на Внешнем объекте будет делегирован реализации стандартов odata (в ODataController), соответствующей стандарту, метода PUT, который я первоначально разместил.

Я надеюсь, что это поможет кому-то в будущем ...

0 голосов
/ 04 июня 2019

Мой подход заключается в добавлении некоторой дополнительной логики в метод, чтобы проверить и посмотреть, существует ли запись в базе данных, используя update.Id, а затем проверить, являются ли данные нулевыми.

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

EDIT Просто заметил метод ProductExists ... Я бы взял это из блока catch и бросил бы его в попытку

//for Post, pass in a 0 for key's argument
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
    {
        if (!ProductExists(key))
        {
            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)
    {
        throw new DbUpdateConcurrencyException(ex.Message);
    }
}
...