Атрибут Asp.Core 2.1 [ApiController]

Я некоторое время не писал API (последний раз в asp.core 2.0), поэтому сегодня решил создать новый в версии .core 2.1 и, к сожалению, обнаружил, что мои действия не работают так, как они работали в предыдущей версии.

Через несколько часов я понял, что проблема связана с [Route("api/[controller]")], и, поскольку [ApiController] не работает без него, я прокомментировал их обоих, и все работает ОТЛИЧНО. ** Так может ли кто-нибудь объяснить мне, что мне делать, чтобы этот код работал с неподключенным «[ApiController]» и теми же вызовами URL-адресов действий?

   //[Route("api/[controller]")]
   //[ApiController]
    public class TestController : ControllerBase
    {
        [HttpGet("api/[controller]")]
        public string A1()
        {
            return "A1()";
        }

        [HttpGet]
        public string A2(int id)
        {
            return $"A2(int {id})";
        }

        [HttpGet]
        public string A3(string p1,string p2)
        {
            return $"A3(string {p1},string {p2})";
        }
        [Route("api/[controller]/A4/{id}")]
        [HttpGet]
        public string A4(int id)
        {
            return $"A4(int {id})";
        }
        [HttpGet("api/[controller]/A5/{id}")]
        public string A5(int id)
        {
            return $"A5(int {id})";
        }
    }

` StartUp.css

   public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{……///code
      app.UseMvc(routes =>
                {
                    routes.MapRoute(
                        name: "default",
                        template: "api/{controller=Home}/{action=Index}/{id?}");
                });
    }
  1. https://локальный: 5001/апи/тест
  2. https://localhost:5001/api/Test/A2?id=1
  3. https://локальный: 5001/апи/тест/A2/1
  4. https://localhost:5001/api/Test/A3?p1=test&p2=test
  5. https://локальный: 5001/апи/тест/A4/1
  6. https://локальный: 5001/апи/тест/а5/1

Тест1: Я внес изменения на основе ответа @chris-pratt

   [Route("api/[controller]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        [HttpGet("")]
        public string A1()
        {
            return "A1()";
        }

        [HttpGet("A2")]
        [HttpGet("A2/{id}")]
        public string A2([FromQuery]int id)
        {
            return $"A2(int {id})";
        }

        [HttpGet("A3")]
        public string A3([FromQuery]string p1, [FromQuery]string p2)
        {
            return $"A3(string {p1},string {p2})";
        }


        [HttpGet("A4")]
        public string A4([FromQuery]int id)
        {
            return $"A4(int {id})";
        }

        [HttpGet("A5")]
        public string A5([FromQuery]int id)
        {
            return $"A5(int {id})";
        }
    }

URL звонков:

  1. в порядке
  2. в порядке
  3. идентификатор = 0
  4. в порядке
  5. 404
  6. 404

5 и 6 теперь одинаковы

для # 3 вы говорите маршрутизатору связать {id} в шаблоне маршрута с параметром метода, но затем указываете параметр метода для привязки к [FromQuery]... и кажется, что [FromQuery] выигрывает ( у вас нет ?id=1 в URL №3). URL-адреса № 4 и № 5 не соответствуют пути маршрута (URL-адрес заканчивается на A4/1, но в маршруте указано «A4» ... вы, вероятно, имели в виду Route («A4/{id}»), а затем удалите это [FromQuery] в сигнатуре метода)

Nick Franceschina 24.01.2019 12:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
1 878
3

Ответы 3

Ни [Route("api/[controller]")], ни [ApiController] не являются вашей реальной проблемой. Атрибут [Route] на уровне класса контроллера указывает префикс маршрута для всех действий в этом контроллере. Когда вы снимаете его, вы возвращаетесь к маршруту по умолчанию в MVC, /{controller}/{action}/{id?}.

Также важно понимать, что использование атрибутивной маршрутизации переопределяет маршрут по умолчанию на уровне применения. Другими словами, если вы не применяете Route к своему контроллеру, то действия там будут использовать маршрут по умолчанию. Если вы примените атрибут Route или один из атрибутов метода HTTP, например HttpGet, к действию, то только это отдельное действие будет использовать настраиваемый маршрут атрибута. Однако, если вы примените атрибут Route к своему контроллеру, файл все действия в контроллере будут использовать маршрутизацию атрибутов, даже если вы явно не применяете атрибут. Поэтому важно, чтобы вы давали каждому действию свой собственный уникальный маршрут.

Ваша первая проблема заключается в том, что вы применили тот же маршрут к некоторым своим действиям, что и к контроллеру. Конечным результатом является то, что маршрут для этого действия на самом деле оказывается /api/test/api/test. Вам нужно только указать часть маршрута после префикса, то есть [HttpGet("a1")]. Если вы хотите, чтобы это был просто префикс без собственного дополнительного сегмента маршрута, вы просто используете пустой маршрут, то есть [HttpGet("")] или просто [HttpGet]. Просто убедитесь, что вы делаете это только один раз для каждого метода HTTP.

Теперь разница с применением [ApiController] заключается в том, что, среди прочего, он переключает привязку по умолчанию с FromForm на FromBody. Однако это будет применяться только к ссылочным типам в качестве параметров, таких как классы. Типы значений, такие как строки, будут несвязанными. Поскольку вы хотите получить их из строки запроса, по-видимому, вам следует добавить к ним [FromQuery]:

    [HttpGet]
    public string A3([FromQuery]string p1, [FromQuery]string p2)

Наконец, у вас также есть куча повторяющихся маршрутов. Как я отметил выше, не указание маршрута равносильно указанию пустого маршрута в действии, что означает, что все, что у вас есть, это префикс маршрута, установленный на контроллере. Вы должны убедиться, что каждое действие имеет уникальный маршрут, на который оно отвечает. Например, приведенный выше метод должен фактически иметь что-то вроде [HttpGet("a3")], что затем даст вам предполагаемый маршрут /api/test/a3?p1=test&p2=test.

Спасибо за ответ и подробное объяснение. К сожалению, если у контроллера есть атрибут [ApiController], то каждое действие должно иметь определенный маршрут, иначе появится ошибка InvalidOperationException. Я внес некоторые изменения в свой код в соответствии с изменениями вашего ответа, которые вы можете увидеть в обновленном вопросе, но все же 3/6 URL-адресов не работают.

volfk 23.01.2019 22:15

Я внес некоторые изменения в ваш код, пожалуйста, посмотрите мое предлагаемое решение. Теперь маршрутизация настроена так, что каждый метод на контроллере будет формироваться как имя маршрута, а теги [FromRoute] в ваших параметрах указывают, что они будут исходить из маршрута, указанного под глаголом HTTP.

[Route("api/[controller]/[action]")]
public class TestController : Controller
{
    public string A1()
    {
        return "A1()";
    }

    [HttpGet]
    [Route("{id}")]
    public string A2([FromRoute]int id)
    {
        return $"A2(int {id})";
    }

    [HttpGet]
    [Route("{p1}/{p2}")]
    public string A3([FromRoute]string p1,[FromRoute]string p2)
    {
        return $"A3(string {p1},string {p2})";
    }

    [HttpGet]
    [Route("{id}")]
    public string A4([FromRoute]int id)
    {
        return $"A4(int {id})";
    }
    [HttpGet("{id}")]
    public string A5([FromRoute]int id)
    {
        return $"A5(int {id})";
    }
}

Теперь ваш маршрут должен выглядеть примерно так: http://локальный: 5200/апи/тест/A2/5 в качестве примера.

Для 5 и 6 вам не нужно использовать [FromQuery]. Для 2 и 3, если вы все еще хотите использовать вместе маршрутизацию строки запроса и атрибута, вам нужно установить для свойства SuppressInferBindingSourcesForParameters значение true, чтобы отключить правила вывода по умолчанию. См. Вывод исходного параметра привязки.

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

services.Configure<ApiBehaviorOptions>(options =>
{ 
   options.SuppressInferBindingSourcesForParameters = true;
});

Контроллер веб-API:

[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
[HttpGet("")]
public string A1()
{
    return "A1()";
}

[HttpGet("A2")]
[HttpGet("A2/{id}")]
public string A2(int id)
{
    return $"A2(int {id})";
}

[HttpGet("A3")]
public string A3([FromQuery]string p1, [FromQuery]string p2)
{
    return $"A3(string {p1},string {p2})";
}


[HttpGet("A4/{id}")]
public string A4(int id)
{
    return $"A4(int {id})";
}

[HttpGet("A5/{id}")]
public string A5(int id)
{
    return $"A5(int {id})";
}
}

Другие вопросы по теме