HttpClient caching in UWP apps - c#

I own both WebApi server (asp.net core app) and the client (UWP app).
I call the WebApi services using HttpClient from the UWP app.
Some resources are readonly and therefore can be cached:
[ResponseCache(Duration = 60*60*12, VaryByQueryKeys = new[] { "id" }, Location = ResponseCacheLocation.Client)]`
[HttpGet("{id}")]
public IActionResult Get(string id) { ... }
Is it possible to enable caching in HttpClient in UWP app or do I have to do it on my own?

The HttpClient has (good) caching itself already. If the default caching behavior is not enough, you can further control the cache through the HttpCacheControl class which separates read and write behavior.
More important is knowing how to use your HttpClient. Even though it implements IDisposable, you should NOT dispose it but keep a single HttpClient object alive through your whole application, it's designed for re-use.
What the HttpClient doesn't do, is return you the cached result while being disconnected. Therefore there are other libraries like Akavache, which creates an offline key-value store.

Related

HttpClient hanging in long-running service

We have a service deployed in AKS that makes HTTP requests to an external API outside of the cluster. The path of the request is AKS -> API Management -> ExpressRoute -> API.
We have been encountering an issue where all requests from an instance of the service will hang when calling the external API. The requests never reach the API. Due to the timeouts that we have configured, the requests will be cancelled after 10 seconds. We most recently saw this on an instance of the service that had been running for over 30 days without any issues. Restarting the instance resolves the issue.
We use Microsoft.Extensions.Http (6.0.0) to register the API client class with the following code:
serviceCollection.AddHttpClient<IMyApiClient, MyApiClient>();
Our API client looks like this:
public class MyApiClient : IMyApiClient
{
private readonly HttpClient _httpClient;
public MyApiClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task CallApi(CancellationToken ctx)
{
await _httpClient.GetAsync("api-uri", ctx);
}
}
Obviously there is complexity in the hops between AKS and the API, however, I first want to understand if we are doing anything wrong with HttpClient. As far as I can tell, we are following the reccommended usage.
We are unable to reproduce this issue locally and we have no additional logs as the requests are simply taking a long time and then being cancelled, there are no actual errors.

Do API controllers use a different instance of a service than Blazor components?

I used this Best way to share data between two child components in Blazor to update a blazorcomponent from a service class to add notifications.
I added this to the POST of an API controller and also injected the notify service into the constructor.
And then display into the Blazor component like this:
But for some reason, this only works with services.AddSingleton, and not services.AddScoped.
But, services.AddScoped does work when you use the notify service between 2 blazor components like this. (This is a different component)
So, does a API controller use a different instance of the notify service? Or am I doing something wrong?
Thank you in advance. if you need more information, please let me know.
Do API controllers use a different instance of a service than Blazor components?
The answer is definitely yes for scoped services, no for singletons, but it gets complicated. But I suspect that's not your real question, and the real question is how to store user-specific data for both. Or modify a specific user's data from an external service
Why the services are different
A new controller instance is created to handle each HTTP request. The HTTP request defines a scope as far as DI is concerned, so a new service instance is created for each HTTP request and dispose after the request is processed.
This is A Good Thing, as it means expensive scoped services like a DbContext are disposed even if an error occurs. In the case of a DbContext, this give transaction-per-request semantics out of the box, without requiring any extra code.
Things are more complicated with Blazor Server. In this case, Blazor Server defines a scope per "user circuit" which is essentially a single tab. This means that scoped objects remain active as long the tab is active when the navigation happens on the client. The MVC part behaves the same way it did with ASP.NET Core MVC/Web API
This is similar to how a desktop application behaves and can cause nasty surprises when people assume that scoped services like a DbContest will be disposed "automagically". When you call SaveChangesAsync you may end up persisting changes you thought were discarded.
So even if you try to use a scoped service in Blazor Server, you may end up with a long-lived service in the Blazor components and a short-lived service in the Web API controllers.
In fact, since different scopes are used, the two services can't even find each other.
How DI and Scope work in Blazor Server
This is documented in ASP.NET Core Blazor dependency injection. The difference in a scope's lifetime is explained in Service Lifetime :
The Blazor Server hosting model supports the Scoped lifetime across HTTP requests but not across SignalR connection/circuit messages among components that are loaded on the client.
The Razor Pages or MVC portion of the app treats scoped services normally and recreates the services on each HTTP request when navigating among pages or views or from a page or view to a component.
Scoped services aren't reconstructed when navigating among components on the client, where the communication to the server takes place over the SignalR connection of the user's circuit, not via HTTP requests.
In the following component scenarios on the client, scoped services are reconstructed because a new circuit is created for the user:
The user closes the browser's window. The user opens a new window and navigates back to the app.
The user closes the last tab of the app in a browser window. The user opens a new tab and navigates back to the app.
The user selects the browser's reload/refresh button.
Notifying a user's tabs after an external call
From the comments :
I have an Azure queue serverless trigger. Inside that function I parse a file and I want to send the status to my blazor project. Once it gets in the API controller, i want to update the UI.
That's tricky. In this case the Web API is essentially acting as another user. The Blazor Server tabs need to be updated in response to what is essentially an external event.
Since it's Blazor Server though, assuming no load balancing is used, all the user circuits are running in the same process that handles API requests. One could raise an "event" in the API controller and have the Blazor components listen to it.
Luckily, that's exactly what libraries like Blazor.EventAggregator have implemented.
The Event Aggregator service is a singleton, registered with :
public void ConfigureServices(IServiceCollection services)
{
services.AddEventAggregator();
}
Assuming the message only accepts a file name and a status:
public record FileEvent(string file,string status);
The Web API Controller will act as a publisher :
class MyController:ControllerBase
{
private IEventAggregator _eventAggregator { get; set; }
public MyController(private IEventAggregator eventAggregator)
{
_eventAggregator=eventAggregator;
}
...
[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Post(BlobInfo blobInfo)
{
await _eventAggregator.PublishAsync(new FileEvent(blogInfo.BlobName,"OK");
return Ok();
}
}
The Blazor Server components that care can inject IEventAggregator, listen for events, specify the events they care about and handle them.
In each component's code-behind, the service can be injected with :
[Inject]
private IEventAggregator _eventAggregator { get; set; }
The class also needs to implement the IHandler<T> interface for the events it cares about :
public class MyComponent : ComponentBase, IHandle<FileEvent>
{
...
List<string> _messages=new List<string>;
public async Task HandleAsync(FileEvent message)
{
_messages.Add($"{message.Name} worked");
await InvokeAsync(StateHasChanged());
}
...
}
The Razor code doesn't really need to change :
#foreach(var value in _messages)
{
<p>#value</p>
}

Using factory method to create typed HTTP client, ASP.NET Core 2.1

I'm following guides/docs on registering HTTP client within my application. There are couple of services I need to call so I decided to go with "Typed clients".
In order to call another service I need to use OAuth - since this is service-to-service call, when I obtain access token, I cache it + I have setup periodical refresh of token. This means there's another component IAccessTokenCache which gives me access token for service.
Thing I'm struggling to figure out is how to register and configure my typed HTTP client providing it also dependency on IAccessTokenCache.
I'm using ASP.NET Core 2.1 (crucial detail, read on).
HTTP client wrapper looks like this (from: HttpClientFactory in ASP.NET Core 2.1 (Part 2) ):
public class ServiceFooClient
{
public ServiceFooClient(HttpClient client)
{
Client = client;
}
public HttpClient Client { get; }
}
And this is how I register and configure client:
services
.AddHttpClient<ServiceFooClient>(
c =>
{
c.BaseAddress = new Uri("https://www.foo.svc");
// TODO: grab particular access token from cache
c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "...");
})
.AddHttpMessageHandler<ResponseMonitorHandler>()
.ConfigureHttpMessageHandlerBuilder(
b =>
{
var handler =
b.AdditionalHandlers.OfType<ResponseMonitorHandler>().FirstOrDefault();
if (handler != null)
{
handler.ServiceName = "Foo Service";
}
});
... I'm already configuring HTTP client and even adding my custom HTTP handler. You can see exact point where I want to access IAccessTokenCache, but I can't.
Possible solutions I can think of:
Configure underlying HttpClient in ServiceFooClient wrapper, like:
// ctor
public ServiceFooClient(HttpClient httpClient, IAccessTokenCache tokenCache)
{
Client = httpClient;
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenCache.GetToken("Foo"));
}
This could work nicely, except I don't configuration decoupling - suddenly dedicated HTTP client has part of configuration in Startup (base URI, additional HTTP handler) and another part in wrapping class (setting authorization header).
Not using AddHttpClient extension method (and others)
I don't really need to call HttpClientFactoryServiceCollectionExtensions.AddHttpClient<T>(...) - I could do all that stuff myself. But as lazy developer... I don't even want to finish this sentence. There's quite a lot of registering inside, so this is just big no no for me.
Upgrade to ASP.NET Core 2.2
Related to previous point - in 2.1, there's no overload of AddHttpClient (2.2: AddHttpClient<TClient>(this IServiceCollection services, Action<IServiceProvider, HttpClient> configureClient)) which would accept callback with service provider. Upgrading to 2.2 would be probably the best solution, yet I will have to be pretty sure that nothing else gets broken (and I already know that there is/was BC break with getting/setting request tracing ID on HTTP context). This could be potentially risky, so I'm trying first to solve my issue in scope of 2.1.
Compare branch of 2.1 with 2.2: HttpClientFactoryServiceCollectionExtensions
Custom HTTP handler setting request headers
Same way as I now register ResponseMonitorHandler, I could register HTTP handler which has access to IAccessTokenCache and sets request authorization header.
But again, as in first case, this decouples configuration of HTTP client. Also if I had several different access tokens, I would either need to implement several HTTP handlers or do some logic deciding what token from cache to use based on request properties.
Finally, question: is there any other way I didn't consider? Is there easy solution of this in ASP.NET 2.1? (... apart of just copy-pasting method from 2.2 of course)
Apparently, there's another extension method in 2.1 ConfigureHttpClient(IHttpClientBuilder, Action<IServiceProvider,HttpClient>) which does exactly what I need!

HttpClient connection issue to WebAPI hosted using Asp.NetCore TestServer

In the unit Test cases, TestServer is used for in-memory hosting of the WebAPI. After this, we are trying to make HttpConnection to this hosted WebAPI using some code like this:
HttpClient client= new HttpClient();
client.BaseAddress("url");
client.GetAsync("url").Result();
This code gives an exception, with the error message
"Connection refused".
But if we get the HttpClient object using the below code and follow the above steps, it works fine.
TestServer.CreateClient()
What could be the reason behind it? Is it because it's an in-memory hosted WebAPI? No actual Http context is there??
That's by design. When you call TestServer.CreateClient() a client with special HttpMessageHandler is created. That handler allows to directly call APIs under test without exposing it as HTTP server:
public class TestServer : IServer, IDisposable
{
public HttpClient CreateClient()
{
HttpClient httpClient = new HttpClient(this.CreateHandler());
...
}
}
That should be faster and suitable enough for unit-testing. For integration testing, run a Kestrel server at test startup.
P.S. I hope your application design became better with DI instead of explicit building. That's nice you resolved the problem this way.

Acquiring Windows user in NancyFx context

I'm being tasked with the creation of an intranet web application. It will eventually consist of a SPA Frontend (probably Angular) sitting on top of a NancyFx API.
Frankly I have absolutely zero experience using Windows users for Auth, and since it's generally not recommended (rightly so), it can be hard to find concrete information on this.
I want to authorize users based on the user that is sending each request. However, I have no idea how I can access user information from each request context in Nancy. I've had a look at doing:
public class IndexModule : NancyModule
{
public IndexModule()
{
Get["/"] = parameters =>
{
var User = this.Context.CurrentUser;
return View["index"];
};
}
}
Unfortunately, this.Context.CurrentUser never seems to be populated and is always null.
How can I populate each request context with a Windows User?
Alternatively, am I doing it wrong? What is the recommended way of dealing with Windows Users in Nancy? I've been researching this for over two days, and I'm quite nervous about telling my boss that I don't know how to handle this rather essential issue.
Nancy provides the NancyContext type to provide you with context within which the call takes place.
Unfortunatelty Nancy does not contain the windows user out of the box. In fact, Nancy has no native knowledge of the IUserPrincipal type, which is what is typically used to represent windows identities.
The good news is that Nancy does integrate with Owin, and Owin provides it's own context which does contain this information, and additionally this will get injected into the Nancy context at no extra cost!
What you need to do to make this happen is install the Nancy.Owin nuget package and then make a call to the extention IAppBuilder.UseNancy() from the Owin "Startup" class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
// other owin config here...
// Register Nancy with Owin middleware...
app.UseNancy();
}
}
This will then enable you to access the Owin environment from the NancyContext like this:
var owinEnv = context.GetOwinEnvironment(); // context = your NancyContext
var windowsUser = (IPrincipal) owinEnv["server.User"];
Alternatively, if you don't fancy Owin, you could do this instead: https://stackoverflow.com/a/28976742/569662

Categories

Resources