Маршрутизация на основе соглашений ASP.Net Core Web API?

Что мне не хватает, что меня встречает 404 для этого контроллера? Я действительно не хочу использовать маршрутизацию на основе атрибутов. Я также не хочу, чтобы action был частью каких-либо URI.

Я использую Visual Studio 2017 и .Net Core 1.1.

TestController.cs

using System;
using Microsoft.AspNetCore.Mvc;

namespace Foo.Controllers
{
    public class TestController : Controller
    {
        public long Get() => DateTimeOffset.Now.ToUnixTimeSeconds();
    }
}

Обратите внимание, что это работает с атрибутом [Route("api/Test")]. Но я не хочу использовать маршрутизацию на основе атрибутов. И как только я снимаю этот атрибут, я получаю 404.

Startup.cs

namespace Foo
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "api/{controller}/{id?}"
                    );
            });
        }
    }
}

Обратите внимание, что здесь также есть кое-что для Autofac / DI, но я убрал его, чтобы не отвлекаться.

Вывод отладки из запроса

Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3308908Z","tags":{"ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.operation.id":"0HL37O0HBESDL","ai.application.ver":"1.0.0.0"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Request starting HTTP/1.1 GET http://localhost:50129/api/test","severityLevel":"Information","properties":{"DeveloperMode":"true","Host":"localhost:50129","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Hosting.Internal.WebHost","Path":"/api/test","Protocol":"HTTP/1.1","Method":"GET","Scheme":"http"}}}}
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:50129/api/test  
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3633954Z","tags":{"ai.cloud.roleInstance":"Desktop","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.location.ip":"::1","ai.operation.id":"0HL37O0HBESDM","ai.application.ver":"1.0.0.0","ai.internal.nodeName":"Desktop","ai.operation.name":"GET /api/test"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Request successfully matched the route with name 'default' and template 'api/{controller}/{id?}'.","severityLevel":"Verbose","properties":{"DeveloperMode":"true","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Routing.RouteBase","RouteName":"default","{OriginalFormat}":"Request successfully matched the route with name '{RouteName}' and template '{RouteTemplate}'.","RouteTemplate":"api/{controller}/{id?}"}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3663952Z","tags":{"ai.cloud.roleInstance":"Desktop","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.location.ip":"::1","ai.operation.id":"0HL37O0HBESDM","ai.application.ver":"1.0.0.0","ai.internal.nodeName":"Desktop","ai.operation.name":"GET /api/test"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"No actions matched the current request","severityLevel":"Verbose","properties":{"DeveloperMode":"true","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Mvc.Internal.MvcRouteHandler","{OriginalFormat}":"No actions matched the current request"}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3693962Z","tags":{"ai.cloud.roleInstance":"Desktop","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.location.ip":"::1","ai.operation.id":"0HL37O0HBESDM","ai.application.ver":"1.0.0.0","ai.internal.nodeName":"Desktop","ai.operation.name":"GET /api/test"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Request did not match any routes.","severityLevel":"Verbose","properties":{"DeveloperMode":"true","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Builder.RouterMiddleware","{OriginalFormat}":"Request did not match any routes."}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3753962Z","tags":{"ai.cloud.roleInstance":"Desktop","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.location.ip":"::1","ai.operation.id":"0HL37O0HBESDM","ai.application.ver":"1.0.0.0","ai.internal.nodeName":"Desktop","ai.operation.name":"GET /api/test"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Connection id \"0HL37O0H95P8K\" completed keep alive response.","severityLevel":"Verbose","properties":{"DeveloperMode":"true","ConnectionId":"0HL37O0H95P8K","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Server.Kestrel","{OriginalFormat}":"Connection id \"{ConnectionId}\" completed keep alive response."}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3878990Z","tags":{"ai.cloud.roleInstance":"Desktop","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.location.ip":"::1","ai.operation.id":"0HL37O0HBESDM","ai.application.ver":"1.0.0.0","ai.internal.nodeName":"Desktop","ai.operation.name":"GET /api/test"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Request finished in 54.7982ms 404","severityLevel":"Information","properties":{"DeveloperMode":"true","ElapsedMilliseconds":"54.7982","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Hosting.Internal.WebHost","StatusCode":"404"}}}}
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 54.7982ms 404

person Matt Thomas    schedule 10.03.2017    source источник
comment
@Nkosi Кроме некоторых вещей Autofac / DI, которые я вынул, это именно так ... Никаких других действий или даже методов в этом контроллере   -  person Matt Thomas    schedule 11.03.2017


Ответы (1)


Это не работает, поскольку метод сопоставления с действием не определен. AFAIK, вы можете достичь маршрутизации, подобной WebApi REST, ТОЛЬКО с помощью маршрутизации атрибутов, и вы можете определить ее на уровне контроллера:

[Route("api/[controller]")]
public class TestController : Controller
{
    [HttpGet]
    public long Get() => DateTimeOffset.Now.ToUnixTimeSeconds();
}

Обновление: обнаружена проблема с github. Веб-API не работает с маршрутизацией на основе соглашений и:

для ASP.NET Core MVC мы решили принять традиционный подход к маршрутизации MVC 5.x, а не подход Web API 2.x. При традиционном подходе к маршрутизации в маршруте должны быть указаны и контроллер, и действие.


Вы можете изменить шаблон маршрута на

template: "api/{controller}/{action}/{id?}"

Но в этом случае ваш URL будет /api/test/get.


Обновление 2 (на основе руководства): вы можете включить пакет NuGet для Microsoft.AspNetCore.Mvc.WebApiCompatShim и по-прежнему использовать ApiController. Код находится на GitHub, если вы любопытно, что он делает. Затем вы можете определить маршрутизацию WebApi:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseMvc(routes =>
    {
        routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
    });
}
person Set    schedule 10.03.2017
comment
@Alex использует template: "api/{controller}/{action}{id?}"? но в данном случае это не стиль WebAPI REST ... - person Set; 10.03.2017
comment
возможно, это не стиль REST, но в любом случае это возможно в ASP.NET Core WebAPI - person Alexan; 10.03.2017
comment
Спасибо, что нашли эту проблему с github. Я взял подсказку из вашего ответа и добавил defaults: new { action = "Get" } к регистрации маршрута в Startup.cs. Теперь он вызывает контроллер без {action} в шаблоне маршрутизации. Однако, если я это сделаю, даже HTTP POST вызовет этот Get метод. Тем не менее, я думаю, что ваш ответ правильный - person Matt Thomas; 11.03.2017
comment
@MattThomas обновил ответ, поскольку обнаружил, что можно использовать поведение маршрутизации Web Api, используя Microsoft.AspNetCore.Mvc.WebApiCompatShim - person Set; 15.03.2017