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

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

0 голосов
/ 23 января 2019

Я давно не писал 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://localhost:5001/api/Test
  2. https://localhost:5001/api/Test/A2?id=1
  3. https://localhost:5001/api/Test/A2/1
  4. https://localhost:5001/api/Test/A3?p1=test&p2=test
  5. https://localhost:5001/api/Test/A4/1
  6. https://localhost:5001/api/Test/a5/1

Test1 : я внес изменения на основе ответа @ 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. id = 0
  4. ок
  5. 404
  6. 404

5 & 6 isто же самое сейчас

Ответы [ 3 ]

0 голосов
/ 24 января 2019

Я внес некоторые изменения в ваш код, пожалуйста, посмотрите мое предлагаемое решение. Маршрутизация теперь настроена так, что каждый метод на контроллере будет формироваться как имя маршрута, а теги [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://localhost:5200/api/test/A2/5 в качестве примера

0 голосов
/ 25 января 2019

Для 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})";
}
}
0 голосов
/ 23 января 2019

Ни [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.

...