The Sharp Ninja
The Sharp Ninja

Reputation: 1041

Blazor: Get `ActionContext`

I'm converting the scaffolded ASP.Net Identity pages from Razor Pages to Blazor. I've hit a snag and hope you can help. ChallengeResult.ExecuteResultAsync requires an instance of ActionContext. Is there an instance of this in a Server-Side Blazor App and how can I get to it?

EDIT

An unhandled exception occurred while processing the request.
InvalidOperationException: Cannot provide a value for property 'ActionContextAccessor' on type 'OCR.UI.Site.Pages.Account.ExternalLogin'. There is no registered service of type 'Microsoft.AspNetCore.Mvc.Infrastructure.IActionContextAccessor'.

Microsoft.AspNetCore.Components.ComponentFactory+<>c__DisplayClass5_0.<CreateInitializer>g__Initialize|2(IServiceProvider serviceProvider, IComponent component)

No joy.

Upvotes: 0

Views: 551

Answers (1)

Gabe Szabo
Gabe Szabo

Reputation: 268

ActionContext, as its name also implies, relates to MVC, specifically "actions", which are the methods that you can call on controllers by correctly addressing their HTTP endpoints.

In an MVC app, you can get the current ActionContext if you register an IActionContextAccessor object to the DI container and try to access its value. The ActionContext describes which controller's which action was hit, and what parameters are bound via routing and model binding.

In MVC, you won't always have an ActionContext, however. In the MVC app's process, you might have other code that does not necessarily get triggered by an MVC controller's action. This might be some background processes you run to send emails, a SignalR hub to communicate realtime with clients, gRPC or just another thread that runs in the app not doing the job of processing the incoming HTTP request. You might even be in the HTTP pipeline, but not MVC! If you have a middleware serving HTTP responses, you won't have an ActionContext. Thus, the accessor pattern. If you don't have an ActionContext, the value will be null.

In Blazor Server, you only have an HTTP request during the initial rendering of the page, or when you are doing static rendering (essentially when you use <component render-mode="Static">, which renders a non-interactive Blazor component, essentially as a string into the HTML output). For this reason, it is never advised to use the ActionContext, even if one is available via the accessor, in Blazor Server. When the user types in the full URL of your page, you will have an ActionContext. But once the user navigates in-app to that same page, there is no HTTP communication whatsoever stemming from that navigation.

In short, in Blazor Server, don't use ActionContext even if you can access one, because it is bad design and it won't work when navigating to that page from another Blazor component.

For your specific problem, you have to go and call different lower-level APIs of the Identity framework (like to check a user's password), and not rely on HTTP. In Blazor Server, your code already runs on the server, in real time connection with the client, responding to the client's interactions. You can't "put down cookies" for instance in the same way you could in MVC, because there is no HTTP response to send the Set-Cookie header in. Thus, you need a server-side rendered solution that does full-postback and a server that handles the incoming requests. That's essentially Razor Pages (or MVC). Because of this, there is no straightforward or out-of-the-box way to simply migrate the Identity scaffolded pages "page-by-page". The interactions have to be repurposed so that they are compatible with Blazor Server, or in the rare cases they are not (which usually come down to needing to set or read server-readable cookies), you might use the aforementioned static rendering mode in Blazor, Razor Pages, or additional client-side logic (like JavaScript) that handles the posting of a form or a full-postback navigation to a URL.

Upvotes: 2

Related Questions