aspnet-api-versioning
aspnet-api-versioning copied to clipboard
System.NullReferenceException: Object reference not set to an instance of an object
Asp.Versioning.Mvc.ApiExplorer:6.0.0-preview.3 netcore:net6
After the UseExceptionHandler is enabled, an "NullReferenceException" is thrown when the interface exception occurs.
Program.cs:
using Asp.Versioning;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddExceptionHandler(options => options.ExceptionHandler= async(http)=> {
http.Response.StatusCode = 200;
await http.Response.WriteAsync("111"); });
builder.Services.AddApiVersioning(option =>
{
option.ReportApiVersions = true;
option.AssumeDefaultVersionWhenUnspecified = true;
option.DefaultApiVersion = new ApiVersion(1, 0);
}).AddMvc().AddApiExplorer(o =>
{
o.GroupNameFormat = "'v'VVV";
o.SubstituteApiVersionInUrl = true;
o.DefaultApiVersion = new ApiVersion(1, 0);
});
var app = builder.Build();
app.UseExceptionHandler();
app.MapControllers();
app.Run();
TestController.cs
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
namespace WebApplication2
{
[Route("api/v1/[controller]")]
[ApiController]
[ApiVersion("1.0")]
public class TestController : ControllerBase
{
private readonly ILogger<TestController> _logger;
public TestController(ILogger<TestController> logger, ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<TestController>();
}
[HttpGet]
public async Task<object> Get()
{
throw new Exception();
return new { test = "1.0" };
}
}
}
error:
System.ObjectDisposedException: The response has been aborted due to an unhandled application exception.
---> System.NullReferenceException: Object reference not set to an instance of an object.
at Asp.Versioning.DefaultApiVersionReporter.Report(HttpResponse response, ApiVersionModel apiVersionModel)
at Asp.Versioning.ReportApiVersionsAttribute.ReportApiVersions(Object state)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.<FireOnStarting>g__ProcessEvents|226_0(HttpProtocol protocol, Stack`1 events)
--- End of inner exception stack trace ---
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.InitializeResponseAsync(Int32 firstWriteByteCount)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.FlushPipeAsync(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.FlushAsync(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Watch.BrowserRefresh.ResponseStreamWrapper.FlushAsync(CancellationToken cancellationToken)
at System.IO.Stream.FlushAsync()
at Microsoft.WebTools.BrowserLink.Net.ScriptInjectionFilterStream.FlushAsync(CancellationToken cancellationToken)
at Microsoft.WebTools.BrowserLink.Net.SendFilesWrapper.StartAsync(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.HttpResponseWritingExtensions.StartAndWriteAsyncAwaited(HttpResponse response, String text, Encoding encoding, CancellationToken cancellationToken, Task startAsyncTask)
The possible cause is the following code in "ExceptionHandlerMiddleware"
private static void ClearHttpContext(HttpContext context)
{
context.Response.Clear();
// An endpoint may have already been set. Since we're going to re-invoke the middleware pipeline we need to reset
// the endpoint and route values to ensure things are re-calculated.
context.SetEndpoint(endpoint: null);
var routeValuesFeature = context.Features.Get<IRouteValuesFeature>();
if (routeValuesFeature != null)
{
routeValuesFeature.RouteValues = null!;
}
}
https://github.com/dotnet/aspnetcore/blob/02646222b7e6fb70a2fce0d3e2545ab3fae31138/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs#L176
Interesting... I'm surprised this has never come up before. When the pipeline is re-executed, I would expect that an Endpoint has been set, but maybe not. This appears to be the only place a NRE could occur where it's not handled. I confirmed that Endpoint.Metadata will never be null. It is surprising that executing the error handler path would yield success without an endpoint.
If you have a repro, great; otherwise, I believe I can create one fairly easily. I'm certain this is a bug, but I'd like a better understanding of the state of things when this happens. API versions should never be reported for an endpoint invoked due to an exception (they're not versioned after all).
@commonsensesoftware https://github.com/saber-wang/WebApplication2/
Confirmed. It's a 🐞 . It will be fixed in the next release. Thanks for trying things out and reporting it.
6.0 has been officially released and contains the fix for this issue. Thanks for reporting it and providing a repro.