I'm refactoring my code to use Refit for my calls to the WebApi service. The interface is set up and I also created a delegating handler:
public class AuthHandler : DelegatingHandler
{
private readonly TokenProvider tokenProvider;
private readonly ISessionStorageService sessionStorage;
public AuthHandler (
TokenProvider tokenProvider,
ISessionStorageService sessionStorage)
{
this.tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider));
this.sessionStorage = sessionStorage ?? throw new ArgumentNullException(nameof(sessionStorage));
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken ct)
{
var someToken = await sessionStorage.GetItemAsync<string>("sometoken");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokenProvider.AccessToken);
request.Headers.Add("someToken", someToken);
return await base.SendAsync(request, ct).ConfigureAwait(false);
}
}
And in Startup.cs
:
services.AddBlazoredSessionStorage();
services.AddScoped<TokenProvider>();
services.AddScoped<AuthHandler>();
services.AddRefitClient<IApiService>().ConfigureHttpClient(options =>
{
options.BaseAddress = new Uri(Configuration["Server:Url"]);
options.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}).AddHttpMessageHandler<AuthHandler>();
I have a razor component and I want to use the service above so I injected the services and did:
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var list = await myService.GetListAsync();
}
}
}
Also, in _Host.cshtml
:
<environment include="Staging,Production">
<component render-mode="ServerPrerendered" type="typeof(App)" param-InitialState="tokens" />
</environment>
<environment include="Development">
<component render-mode="Server" type="typeof(App)" param-InitialState="tokens" />
</environment>
However I get the following exception:
System.InvalidOperationException: JavaScript interop calls cannot be issued at this time. This is because the component is being statically rendered. When prerendering is enabled, JavaScript interop calls can only be performed during the OnAfterRenderAsync lifecycle method.
So, just to make sure if I have values or not I added the following before the call to the api:
var someToken = await sessionStorage.GetItemAsync<string>("sometoken");
var accessToken = tokenProvider.AccessToken;
And I DO have values in both variables.
So why can't I access the session storage from the delegating handler? And why is token provider instance instantiated but the properties all null (also in the handler)?
EDIT
I only need one place to keep my tokens. It doesn't matter if it's the token provider or the session storage, as long as it works in blazor pages/componenents and other services.
UPDATE 1
One can skip DI and create the service like this:
var service = RestService.For<IMyService>(new HttpClient(new AuthHandler(tokenProvider, sessionStorage))
{
BaseAddress = new Uri(myUrl)
}
);
This will work as expected. However, it would be much more better to use DI. The problem might either be in Blazor or in Refit.