Skip to content

Api Versioning break CreatedAt on MVC Core 2.2Β #409

Closed
@vanbukin

Description

@vanbukin

If I make GET request on http://localhost:5000/api/v1/values/42, then I've got the following result

{
    "value": "model 42"
}

and it's ok. Bui if I make a POST on http://localhost:5000/api/v1/values
with following body

{
    "value": 42
}

I've got the following stacktrace

fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HLIS5H63UVVC", Request id "0HLIS5H63UVVC:00000003": An unhandled exception was thrown by the application.
System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.AspNetCore.Mvc.Routing.ApiVersionRouteConstraint.Match(HttpContext httpContext, IRouter route, String routeKey, RouteValueDictionary values, RouteDirection routeDirection)
   at Microsoft.AspNetCore.Routing.Template.TemplateBinder.TryProcessConstraints(HttpContext httpContext, RouteValueDictionary combinedValues, String& parameterName, IRouteConstraint& constraint)
   at Microsoft.AspNetCore.Routing.DefaultLinkGenerator.TryProcessTemplate(HttpContext httpContext, RouteEndpoint endpoint, RouteValueDictionary values, RouteValueDictionary ambientValues, LinkOptions options, ValueTuple`2& result)
   at Microsoft.AspNetCore.Routing.DefaultLinkGenerator.GetPathByEndpoints(List`1 endpoints, RouteValueDictionary values, RouteValueDictionary ambientValues, PathString pathBase, FragmentString fragment, LinkOptions options)
   at Microsoft.AspNetCore.Routing.DefaultLinkGenerator.GetPathByAddress[TAddress](HttpContext httpContext, TAddress address, RouteValueDictionary values, RouteValueDictionary ambientValues, Nullable`1 pathBase, FragmentString fragment, LinkOptions options)
   at Microsoft.AspNetCore.Routing.LinkGeneratorRouteValuesAddressExtensions.GetPathByRouteValues(LinkGenerator generator, HttpContext httpContext, String routeName, Object values, Nullable`1 pathBase, FragmentString fragment, LinkOptions options)
   at Microsoft.AspNetCore.Mvc.Routing.EndpointRoutingUrlHelper.Action(UrlActionContext urlActionContext)
   at Microsoft.AspNetCore.Mvc.UrlHelperExtensions.Action(IUrlHelper helper, String action, String controller, Object values, String protocol, String host, String fragment)
   at Microsoft.AspNetCore.Mvc.UrlHelperExtensions.Action(IUrlHelper helper, String action, String controller, Object values, String protocol, String host)
   at Microsoft.AspNetCore.Mvc.CreatedAtActionResult.OnFormatting(ActionContext context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsync(ActionContext context, ObjectResult result)
   at Microsoft.AspNetCore.Mvc.ObjectResult.ExecuteResultAsync(ActionContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult result)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResultFilterAsync[TFilter,TFilterAsync]()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResultExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultFilters()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 191.4428ms 500

I can put breakpoint inside Post action and see that it calls correctly. The problem is somewhere inside CreatedAtAction.

Here is minimal repro
.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="3.0.0-beta2" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="3.0.0-beta2" />
  </ItemGroup>

</Project>

and Program.cs

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;

namespace ApiVersionTrouble
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args)
        {
            return WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
        }
    }

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            var builder = services.AddMvc();
            builder.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            services.AddApiVersioning();
            services.AddVersionedApiExplorer();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseMvc();
        }
    }

    [ApiController]
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/values")]
    [Produces("application/json")]
    public class ValuesController : ControllerBase
    {
        [HttpGet("{settingsId:long}")]
        public ValueResponse Get([FromRoute] long settingsId)
        {
            return new ValueResponse(settingsId);
        }

        [HttpPost]
        public ActionResult Post([FromBody] ValueInput model)
        {
            var response = new ValueResponse(model.Value);
            return CreatedAtAction(
                nameof(Get),
                new {version = "1", settingsId = model.Value},
                response);
        }
    }

    public class ValueInput
    {
        public long Value { get; set; }
    }

    public class ValueResponse
    {
        public ValueResponse(long id)
        {
            Value = "model " + id.ToString("D");
        }

        public string Value { get; }
    }
}

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions