HttpRequestScope.HttpRequest is not set when RoutePrefix is an empty string.
jusbuc2k opened this issue · comments
Assemblies affected
8.2.3.0
Describe the bug
HttpRequestScope.HttpRequest is not set when RoutePrefix is an empty string.
Reproduce steps
Create an OData route component with an empty string as the route prefix (not null).
Probable Cause
The bug is in this line of code. Assuming we are supposed to be able to use an empty string as a route prefix, which works everywhere else but here, this code is using string.IsNullOrEmpty
when I think it should be using != null
.
/// <summary>
/// Create a scoped request.
/// </summary>
/// <param name="request">The <see cref="HttpRequest"/> instance to extend.</param>
/// <param name="routePrefix">The route prefix for this request. Should match an entry in <see cref="ODataOptions.RouteComponents"/>.</param>
/// <returns></returns>
private static IServiceScope CreateRequestScope(this HttpRequest request, string routePrefix)
{
ODataOptions options = request.ODataOptions();
IServiceProvider rootContainer = options.GetRouteServices(routePrefix);
IServiceScope scope = rootContainer.GetRequiredService<IServiceScopeFactory>().CreateScope();
// Bind scoping request into the OData container.
if (!string.IsNullOrEmpty(routePrefix))
{
scope.ServiceProvider.GetRequiredService<HttpRequestScope>().HttpRequest = request;
}
return scope;
}
from
While the OData calls seem to work fine with a vanilla configuration, this causes a null reference exception in custom filter binders that I am building that depend on HttpRequestScope.HttpRequest
because I need to get services from the HttpContext.RequestServices
container. If there's a better way to do this, I can't find it.
Edit: I'm adding a simplified version of my Startup code to show AddRouteComponents
services.AddControllers().AddOData(options =>
{
options.AddRouteComponents("", ODataModels.GetDefaultModel(100, 100), services =>
{
services.AddSingleton<IStreamBasedJsonWriterFactory>(_ => DefaultStreamBasedJsonWriterFactory.Default);
services.AddSingleton<ODataBatchHandler, DefaultODataBatchHandler>();
// this is a custom service that my custom filter binders depend on
services.AddScoped<IOpenSchemaProvider>(sp =>
{
var requestScope = sp.GetRequiredService<HttpRequestScope>();
if (requestScope.HttpRequest == null)
{
throw new Exception("HttpRequest is null on OData HttpRequestScope.");
}
var schemaProvider = requestScope
.HttpRequest
.HttpContext
.RequestServices
.GetRequiredService<IOpenSchemaProvider>();
return schemaProvider;
});
});
});
It seems "non-sense" to me also to test "isNullOrEmpty".
By the way, why don't you use 'sp' directly to get the "IOpenSchemaProvider"?
The IOpenSchemaProvider
implementation is registered with the ASP.NET service container. Unless I'm mistaken, I thought the service provider from which custom filter binders are created doesn't resolve services out of the ASP.NET one, but only the route services?