ASP. NET POST-версия WebAPI 2 с идентификатором - PullRequest
2 голосов
/ 16 марта 2020

Я пытаюсь реализовать nuget «Microsoft.As pNet .WebApi.Versioning» в существующем продуктивном приложении WebAPI 2 для поддержки новых разработанных контроллеров «v2».

Пока все работает нормально с большинством контроллеров, но теперь у меня возникли проблемы с некоторыми контроллерами POST.

Пример кода:

using Microsoft.Web.Http;
using System.Web.Http;

namespace ApplicationApi.Controllers
{
    [ApiVersion("1.0")]
    [Route("api/version")]
    public class VersionController : ApiController
    {
        //Works
        [HttpGet]
        public IHttpActionResult Get()
        {
            return Ok(true);
        }

        //Throws  HTTP 405 - The requested resource with API version '1.0' does not support HTTP method 'POST'.
        [HttpPost]
        public IHttpActionResult Post(int id, [FromBody]dynamic value)
        {
            return Ok(true);
        }

        //Works
        [HttpPost]
        public IHttpActionResult Post([FromBody]dynamic value)
        {
            return Ok(true);
        }
    }
}

Все методы POST без дополнительной {id} работы. Но так как у нас в течение долгого времени огромное количество старых клиентов, я должен поддерживать и запускать старую версию, а удаление старых идентификаторов {id} из старых контроллеров невозможно. В новых контроллерах не требуется {id}.

Конфигурация WebAPI

var constraintResolver = new DefaultInlineConstraintResolver()
{
    ConstraintMap =
    {
        ["apiVersion"] = typeof( ApiVersionRouteConstraint )
    }
};
config.MapHttpAttributeRoutes(constraintResolver);

config.AddApiVersioning(options =>
{
    options.AssumeDefaultVersionWhenUnspecified = true;
});

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

Есть идеи, что я делаю неправильно или как обойти это?

Ответы [ 3 ]

4 голосов
/ 21 марта 2020

Проблема не имеет отношения к управлению версиями.

Причина, по которой методы с параметром id не работают, заключается в том, что у вас маршруты конфликтов :

[Route("api/version")]

и

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

Для контроллера, называемого Version с атрибутом Route("api/version"), вы можете иметь только один атрибут маршрутизации, ИЛИ обычная маршрутизация (MVC / WebAPI маршрутизация), но вы не можете иметь оба.

В вашем случае атрибут маршрутизации переопределяет MVC, поэтому api/version/{id} больше не существует. Но api/version?id все еще работает, потому что он решается с помощью атрибутов маршрутизации.

Чтобы исправить эту проблему , вы можете изменить маршрутизацию атрибутов на контроллере на RoutePrefix вместо Route и при необходимости используйте Route с относительным путем к каждому действию.

обязательно означает, что у вас есть маршруты, к которым нельзя обратиться с помощью обычной маршрутизации.

[ApiVersion("1.0")]
[RoutePrefix("api/version")]
public class VersionController : ApiController
{
    [HttpPost]
    public IHttpActionResult Post([FromBody] dynamic value)
    {
        return Ok();
    }

    [HttpPost]
    public IHttpActionResult Post(int id, [FromBody] dynamic value)
    {
        return Ok(1);
    }
}
1 голос
/ 21 марта 2020

Попробуйте

[ApiVersion("1.0")]
public class VersionController : ApiController
{
    //Works
    [HttpGet]
    [Route("api/version")]
    public IHttpActionResult Get()
    {
        return Ok(true);
    }

    //Throws  HTTP 405 - The requested resource with API version '1.0' does not support HTTP method 'POST'.
    [HttpPost]
    [Route("api/version/{id}")]
    public IHttpActionResult Post([FromUri]int id, [FromBody]dynamic value)
    {
        return Ok(true);
    }

    //Works
    [HttpPost]
    [Route("api/version")]
    public IHttpActionResult Post([FromBody]dynamic value)
    {
        return Ok(true);
    }
}

ИЛИ

[ApiVersion("1.0")]
[RoutePrefix("api/version/")]
public class VersionController : ApiController
{
    //Works
    [HttpGet]
    public IHttpActionResult Get()
    {
        return Ok(true);
    }

    //Throws  HTTP 405 - The requested resource with API version '1.0' does not support HTTP method 'POST'.
    [HttpPost]
    [Route("{id}")]
    public IHttpActionResult Post([FromUri]int id, [FromBody]dynamic value)
    {
        return Ok(true);
    }

    //Works
    [HttpPost]
    public IHttpActionResult Post([FromBody]dynamic value)
    {
        return Ok(true);
    }
}
1 голос
/ 19 марта 2020

Проблема в том, что у вас есть 2 метода post, и API не знает, как определить / сгенерировать имя ресурса.

Что вам нужно сделать, это добавить настроенное имя ресурса с дублированным именем метода.

Итак, если мы возьмем, к примеру, ваш пример, вы можете добавить ресурс к тому, который выбрасывает исключение, и оставить другое, или дать им оба уникальное имя ресурса.

Например, что-то вроде [HttpPost("id")].

Чтобы подтвердить это, я сделал тестовый проект с вашим кодом, для этого я использовал swagger, и он потерпел неудачу. Теперь, когда я добавил id к одному из методов сообщения, подобных этому:

[HttpPost("id")]
public IActionResult Post(int id, [FromBody]dynamic value)
{
    return Ok(true);
}

[HttpPost]
public IActionResult Post([FromBody]dynamic value)
{
    return Ok(true);
}

Затем он работает со следующим выводом:

enter image description here

Дополнение к основному ответу В своем коде вы разрешаете принимать параметр, добавляя int id в свой метод

 Post(int id, [FromBody]dynamic value)

Если вы действительно не хотите использовать Параметр, то вы можете передать вам настроенный объектный класс и сделать что-то вроде этого, не тестируя его:

 Post([FromBody]MyCustomObjectClass data)

и вы MyCustomerObjectClass:

class MyCustomObjectClass {
    public int? Id { get; set; }
    public dynamic Value { get; set; }
}

, чем вам не нужно 2 Методы записи, вы можете иметь только один, и Id может быть необязательным.

И только чтобы вас не смущать. Это плохо, что я использовал id в посте вроде [HttpPost("id")] в качестве соглашения об именах, он не имеет отношения к параметру Id вашего метода. id в данном случае это просто название ресурса, оно не влияет на действия. так что вы можете называть это тем, что когда-либо подходит вашим логикам c [HttpPost("post1")] и [HttpPost("post2")].

Последний совет: хорошо использовать Dynami c для тестирования и быстрого запуска API. , но я бы всегда использовал объект типа stati c.

Я бы настоятельно рекомендовал взглянуть на this .

...