Blazor Server HttpContext is null when published on local IIS - asp.net-core

In my Blazor Server app I have this code in a component that needs to read cookies from the Request (so I would read them before the render):
[Inject] private IHttpContextAccessor HttpCxAccessor { get; set; }
...
protected override void OnInitialized()
{
var context = HttpCxAccessor.HttpContext;
// context is null when on Local IIS
the code works when I run it from VS (IISExpress) but when I publish it on local IIS, the HttpContext is null

You shouldn't use HttpContextAccessor in Blazor Server because the Blazor Server works outside the .NetCore pipeline and basically there is no guarantee that you will have access to the desired amount of HttpContext everywhere for more info you can refer to this issue. However, If you have to use the HttpContext then you have to get the desired value(s) from HttpContext when rendering _Host.cshtml and save it in a variable and use that variable in the form of Cascading Parameters in the components in the rest of the program.
An Example of implementation is here.

Related

Replace the injected HttpContext for class in application

I'm setting up some integration tests for mediatr handlers using xunit, respawn, and webapplicationfactory....
One of classes ultimately called by the mediatr handler that is being tested.. has a dependency on IHttpContextAccessor as you can see below
I feel like I've set up the "replacement" singleton that I want injected correctly as per below:
But when the integration test runs.. the httpContextAccessor is not null but the HttpContext is ALWAYS null.
I've tried about 6 million things :( Much sadness.
How can get the IHttpContextAccessor to resolve correctly to what I'm setting in ConfigureServices?
When the integration test runs.. the httpContextAccessor is not null but the HttpContext is ALWAYS null.How can get the IHttpContextAccessor to resolve correctly to what I'm
setting in ConfigureServices?
No we cannot, The HttpContext will only be available within the scope of the request because ConfigureServices invocked before constructing Startup so if we try to inject, it will through exception, even if we declare at global variable in that scenario it will always be null. Application startup happens only once, and long before any request has been received.
Let's try to access it within IConfiguration as following:
public Startup(IConfiguration configuration,IHttpContextAccessor httpContextAccessor)
{
Configuration = configuration;
HttpContextAccessor = httpContextAccessor;
}
public IConfiguration Configuration { get; }
public IHttpContextAccessor HttpContextAccessor { get; }
Output:
As you can see, it doesn't allow us to do so.
Even if we try to inject it within configuration itself it will ended up with following exception:
Therefore, ConfigureServices runs once before any httprequest coming to application. If we forcefully try to call it outside of request life-cycle it will always ended up with NullReferenceException. You can get more details in the official document.
In addition to this, we can inject service reference within configuration as following:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
I needed to implement IHttpContextAccessor myself
Then utilise in the ConfigureTestServices of the WebApplicationFactory ConfigureWebHost

Distributed IdentityServer4 .net core (multi instance) issues HttpContext must not be null

So we have a setup of IdentityServer4 with .net core, on only one instance everything works as expected, however when we decided to spin more instances of Identity Server, we randomly got issues when logging in or out from the client.
I followed these docs: Distributed IdentityServer
This is how I am adding IDS4
_identityBuilder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiResources(Configuration.GetSection("idServer:apiResources"))
.AddInMemoryApiScopes(Configuration.GetSection("idServer:apiScopes"))
.AddInMemoryClients(Configuration.GetSection("idServer:clients"))
.AddAspNetIdentity<HeimdallUserEntity>()
;
Also because the server will be distributed I also added this code, note that certificate below is shared between the instances (so every instance uses the same certificate)
_identityBuilder.AddSigningCredential(certificate);
services.AddDataProtection()
.SetApplicationName(Assembly.GetExecutingAssembly().FullName)
.PersistKeysToDbContext<MainDbContext>()
.ProtectKeysWithCertificate(certificate);
However even with this setup I am having issues (randomly) while logging in and out from the Client which uses PKCE. The issue i am having is i am getting this exception randomly:
HttpContext must not be null.
Which is being thrown from: Microsoft.AspNetCore.Identity -> SignInManager -> SignOutAsync()
and from: Microsoft.AspNetCore.Identity -> SignInManager -> SignInWithClaimsAsync(TUser user, AuthenticationProperties authenticationProperties, IEnumerable additionalClaims)
This exception is handled and thrown in the SignInManager.cs class right here:
public HttpContext Context
{
get
{
var context = _context ?? _contextAccessor?.HttpContext;
if (context == null)
{
throw new InvalidOperationException("HttpContext must not be null.");
}
return context;
}
set
{
_context = value;
}
}
Also note that the client_credentials work normally, I request a token and everything works fine with multiple instances/replicas.
:: UPDATE ::
I have finally found the issue, which has nothing to do with Identity Server, in our system we use Microsoft Orleans, and we have a Grain which injects the UserService and, the UserService injects the SignInManager, turns out the SignInManager requires the HttpContext to be able to resolve services, but since orleans does not provide an IHttpContextAccessor, the HttpContext can never be resolved :/
For now we are calling the UserService directly. But it would be nice to be able to create/find a SignInManager which would not depend on HttpContext (especially since it only uses it to resolve other services)
Are you using the same token signing credentials across the different instances and adding them using the AddSigningCredential method?
Don't think this is the core issue here but you should be aware of that when you use the PersistKeysToDbContext and share it across services, then there might also be a race condition when multiple services tries to write to the same table in the database. Especially at startup (when the DB is empty) and when the keys are rotated every 90 days.

.NET Core DI passing info of AddHttpContextAccessor to a service

In my solution I have projects for my API, my Web App and also have another project which includes services, that are getting some information from a database and formatting them, these are currently only used by this API, but these could be used by other API projects in the future.
My API have a couple controllers that are returning JSON data from the result returned by the services.
In some cases the services needs to call the API to process some information before calling the request to the database. Since I have dev/staging/prod environment with their own URL I don't want to hardcode the URLs in the services I want to use DI to get these dynamicaly depending on the context.
In the Startup.cs of my API I have added services.AddHttpContextAccessor(); in the ConfigureServices(IServiceCollection services) section to gain access to the current http context :
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
...
}
With that I know I can now access the information directly into my controller which I tried and it worked :
public class DataController : ControllerBase
{
...
private readonly string _baseUrl;
public FeaturesController(...
,IHttpContextAccessor httpContextAccessor)
{
...
_baseUrl = UrlHelpers.ShowBaseURL(httpContextAccessor) ?? throw new ArgumentNullException(nameof(_baseUrl));
}
}
public static class UrlHelpers
{
public static string ShowBaseURL(IHttpContextAccessor httpcontextaccessor)
{
var request = httpcontextaccessor.HttpContext.Request;
var absoluteUri = string.Concat(
request.Scheme,
"://",
request.Host.ToUriComponent(),
request.PathBase.ToUriComponent());
return absoluteUri;
}
}
I could do just about the same thing in the services but to me they should not act directly on the httpcontext, since this is not the job they are meant to do. I am sure I could do better by adding a class injected of some sort that would have then make the specific value available to my services.
I know I could also pass the _baseUrl directly as an argument when calling the services from my controller but since I am trying to better understand DI and use it I would rather find another way if it is viable.
I can't give credit but I went with Steven solution which make the most sens

How do I get the website URL or access the HttpContext from a IHostedService in ASP.NET Core?

In our MultiTenant ASP.NET Core 2.2 app, we determine the tenant from the URI.
How can get the website URL from an IHostedService?
The HttpContext is always null.
The IHttpContextAccessor.HttpContext IS ALWAYS NULL
public MyHostedService(ILogger<TurnTimeTask> logger,
IHttpContextAccessor httpContextAccessor)
{
_logger = logger;
_httpContextAccessor = httpContextAccessor;
}
Even running the IHostedService in Scope also returns NULL for the httpContextAccessor.HttpContext
i.e. Injecting it through a Scoped Service doesn't work either.
public override Task ProcessInScope(IServiceProvider serviceProvider)
{
var request = _httpContextAccessor?.HttpContext?.Request;
//request is always null
}
Is there any other way to get the website's URL from an IHostedService?
HttpContext is populated when a http request hits your site (very simple explanation).
Think of a IHostedService as something that runs in the background independent of any http requests, it runs in a completely different context than for example the requests that hits your controllers.
HttpContext is heavily tied to ASP.NET Core while IHostedService does not need ASP.NET Core to run.

How create a middleware with api endpoints in .NET Core

I have created the web application with the web api. The application contains some Controllers for example TodoController:
namespace TodoApi.Controllers
{
[Route("api/[controller]")]
public class TodoController : Controller
{
private readonly TodoContext _context;
public TodoController(TodoContext context)
{
_context = context;
}
[HttpGet]
public IEnumerable<TodoItem> GetAll()
{
return _context.TodoItems.ToList();
}
}
}
If I create the GET request - /api/todo - I get the list of Todos from database.
I have a list of controllers and api endpoints like above.
I would like distribute this api to another application ideally like middleware - my idea is register in Startup.cs like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddTodoApi();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseTodoApi();
}
This will be awesome use case for my api but I don't know how this controllers api endpoints rewrite like middleware and return same JSON data same approache like using classic Controllers.
How can I write the middleware in .NET Core for creating API endpoints?
Instead of the separate middleware, you may configure the MVC middleware to discovery controllers from another assembly:
// using System.Reflection;
public void ConfigureServices(IServiceCollection services)
{
...
services
.AddMvc()
.AddApplicationPart(typeof(TodoController).GetTypeInfo().Assembly);
Controllers are part of MVC middleware, they are not a separate part of request pipeline (but this is what middlewares are). When you register the custom middleware, it by default invokes on each request and you have HttpContext context as an input parameter to work with/edit
Request/Response data. But ASP.NET Core provides Map* extensions that are used as a convention for branching the pipeline.
Map branches the request pipeline based on matches of the given request path. If the request path starts with the given path, the branch is executed.
Example:
private static void HandleMapTodo(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("/api/todo was handled");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map("/api/todo", HandleMapTodo);
}
Note, that as middleware knows nothing about MVC middleware, you have only access to "raw" request and do not have features like model binding or MVC action filters.
Because it looks like the perfect microservices approach (similar than what my team is doing right now) I'd create a client assembly that can consume your API, the one that contains your TodoController, if you define a contract, and interface, for that API you can register it in your other assembly as it was a midleware and also you could mock that behaviour in your unit tests.
So, as I said, you could inject your client in ConfigureServices method, you can create:
public static IServiceCollection AddTodoRestClient(this IServiceCollection services)
{
services.AddSingleton<ITodoRestClient, TodoRestClient>();
return services;
}
Also consider that you will need to provide the enpoint so, it might looks like:
public static IServiceCollection AddConfiguredTodoClient(this IServiceCollection services, string todoEndpoint)
{
AddTodoClient(services);
ITodoRestClient todoRestClient = services.BuildServiceProvider().GetService<ITodoRestClient>();
// Imagine you have a configure method...
todoRestClient.Configure(services, todoEndpoint);
return services;
}
You can create those methods in a TodoRestClientInjector class and use them in Configure method on your startup.
I hope it helps
--- MORE DETAILS TO ANSWER COMMENTS ---
For me TodoClient is a Rest client library that implements calls to the ToDo API, (I've edited previous code to be TodoRestClient) methos like, i.e., CreateTodoItem(TodoDto todoItem) which implementation would call to the TodoController.Post([FromBody] item) or GetTodos() which wuold call TodoController.Get() and so on and so forth....
Regarding the enpoints... This approach implies to have (at least) two different applications (.NET Core apps), on the one hand the ASP NET Core app that has your TodoController and on the other hand a console application or another ASP NET Core API on which startup class you'll do the inyection adn the Rest client (the Todo Rest client) configuration ...
In a microservices approach using docker, in a dev environment, you'll use docker-compose-yml, but in a traditional approach you'll use concrete ports to define the endpoints...
So, imagine that you have in the second service a controller that need to use TodoController, to achieve so I'll use the above aproach and the "SecondController" would look like:
public class SecondController : Controller
{
private readonly SecondContext _context;
private readonly TodoRestClient _todoRestClient;
public TodoController(SecondContext context, ITodoRestClient todoRestClient)
{
_context = context;
_todoRestClient= todoRestClient;
}
// Whatever logic in this second controller... but the usage would be like:
_todoRestClient.GetTodos()
}
Just few final hints: it's key to minimize calls between services because it increases latency, and more and more if this happens on cascade. Also consider Docker usage, looks challenging but it is quite easy to start and, indeed, is thought to be used in scenarios that the one you presented and solutions like mine.
Again, I hope it helps.
Juan