I am making a DLL to consume a REST API in aspnetcore.
Ideally, I would like it to be accessed this way:
API api = new API(clientInfo);
api.Module.Entity.Action(params);
But I am struggling to make that a reality. I can't make anything static because more than 1 session might be instanced at the same time. I can't pass the session around except by reference otherwise session state(cookies etc.) might change in the copy. Is there a design pattern I should be using?
public class API
{
private Session _session;
public API(ClientInfo clientInfo)
{
_session = new Session(clientInfo);
}
}
The session serves as middleware for the client, stores login data in case the client needs to repeat login, handles some errors/retries and exposes client methods.
public class Session
{
private Client _client;
private string _path;
public Session(ClientInfo clientInfo)
{
_client= new Client(clientInfo);
_path = clientInfo.Path;
}
public HttpResponseMessage Get(string name, string arguments = "")
{
return _client.Get(_path, name, arguments);
}
...
}
The client actually performs the calls.
public class Client
{
public HttpResponseMessage Get(string path, string endpointName, string arguments)
{
return GetClient().GetAsync(path + endpointName + arguments).Result;
}
private HttpClient GetClient(){...}
...
}
Personally, I just build a simple client for my APIs, with methods corresponding to the endpoints the API has:
public class FooClient
{
private readonly HttpClient _httpClient;
public FooClient(HttpClient httpClient)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public async Task<GetFooResult> Get(int id)
{
...
}
// etc
}
The HttpClient dependency is provided by registering a typed client in Startup.cs:
services.AddHttpClient<FooClient>(c =>
{
// configure client
});
And I add an IServiceCollection extension to encapsulate this and any other setup logic:
public static class IServiceCollectionExtensions
{
public static IServiceCollection AddFooClient(this IServiceCollection services, string uri)
{
...
}
}
Then, in my Startup, I can simply do something like:
services.AddFooClient(Configuration.GetValue<string>("FooUri"));
This is extremely helpful for setting up automatic error handling, retry policies, etc. via Polly, as you can then set up all that configuration just once in the extension.
Now, getting to your issue of persisting things like auth tokens, you have a few possibilities. I tend to prefer persisting auth tokens as claims, in which case you can simply retrieve the claim and pass it into methods on your client that need it:
var foo = await _fooClient.Get(fooId, User.FindFirstValue("FooAuthToken"));
If you handle things that way, you can bind your client in any scope, including singleton.
An alternative approach would be to actually persist the auth token in your client, but this has to be done with care. You should definitely avoid using singleton scope, unless you're employing something like a ConcurrentDictionary and even then, ensuring that the right token is always used could be a bit gnarly.
Assuming you're using a request scope, you can store the token directly as an ivar or something, but you'd still need to persist it some place else beyond that, or you'd still need to re-auth for each request. If you were to store it in the session, for example, then you could do something like:
services.AddScoped<FooClient>(p =>
{
var httpClientFactory = p.GetRequiredService<IHttpClientFactory>();
var httpContextAccessor = p.GetRequiredService<IHttpContextAccessor>();
var httpClient = httpClientFactory.Create("ClientName");
var session = httpContextAccessor.HttpContext.Session;
var client = new FooClient(httpClient);
client.SetAuthToken(session["FooAuthToken"]);
});
However, even then, I'd still say it's better to pass the auth token into the method than do any of this. It's more explicit about which actions require auth versus those that do not, and you always know exactly what's coming from where.
One of your biggest problems will be the reuse of the HttpClient. This is a known problem for "pre-Core" days. Luckily, its been addressed and as of Net Core 2.1 we now have an HttpClientFactory which allows you to spin up as manage HttpClients as you need and they're handled for you as part of the framework.
https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore
With this in mind, theres nothing stopping you from using DI to inject an IHttpClientFactory which will provide you with the necessary access to the pipeline you need. Other than that, its entirely up to you how you design the code which "manages" your access to the REST resources. Maybe some sort of Repository Pattern? (Purely guess work really without knowing your architecture etc)
Related
Whenever a client makes a request to my ASP .NET Core 3.1 API, I want to use a HttpClient to do some actions in other services.
I registered HttpClient with Dependency Injection:
services.AddHttpClient<MyHttp>();
Unfortunately the BaseAddressof this HttpClient is not static. Instead the client sends a custom header including a reference to a database-entry.
So in order to determine the BaseAddress for my HttpClient i need to make an async call to my Database.
Currently I do something like the following:
public class SomeController
{
public SomeController(MyHttp http, AddressRepository db)
{
_http = http;
_db = db;
}
public async Task<dynamic> SomeAction([FromRoute] string id)
{
var address = await _db.Get(id);
_http.BaseAddress = new Uri(address);
var res = await _http.GetAsync("some-path");
//Handle response and do some business-logic
return new { };
}
}
This works, but whenever i use MyHttp in any service I need to make sure that this services sets the BaseAddress property.
I'd rather provide the Address as a constructor parameter.
First i thought of a factory implementation. But services.AddScoped(serviceProvider => {}) does not support async operations (And I understand that object initialization should be fast and reliable).
But I feel like my current solution is also a bad practice. Is there any better way of doing this?
That is right that service collection registration does not support async factory - because service resolving should be always fast.
But there is nothing wrong with initializing baseAddress after client creation. So if your wanna make sure that httpClient is initialized with correct base address how about extracting that logic into separate class that will build your http client configuration?
public YourHttpClientFactory {
private IHttpClientFactory _factory;
public YourHttpClientFactory(IHttpClientFactory factory)
{
_factory = factory;
}
public Task<HttpClient> Create(Guid id) {
var client = factory.createClient();
// do your async client initialization
return client;
}
}
I am trying to abstract away the create of an AmazonS3Client. For context this is done using this mechanism rather than the standard DI mechanism as the credentials needed for the client are only known at runtime after the credentials have been retrieved to assume a role. These credentials have a temporary access token.
I have created the following factory, which aims to return the same client to the caller until the credentials used within the client are invalid. After this I dispose of the client and instantiate a new one.
I want to ensure this is thread safe. As in caller one could have got the client and was about to use it. At the same time caller two tries to get a client, which was then deemed to have invalid credentials. The logic below then tries to dispose of the client. So I am concerned on the impact on caller one.
To add the following code belongs in a singleton service within .net core.
public class AmazonS3ClientFactory : IAmazonS3ClientFactory
{
private readonly IAmazonAssumeRoleService assumeRoleService;
private readonly Config config;
public AmazonS3ClientFactory(
IOptions<Config> config,
IAmazonAssumeRoleService assumeRoleService)
{
if (string.IsNullOrWhiteSpace(config?.Value.AssumedRoleArn))
{
throw new ArgumentNullException(nameof(config));
}
this.config = config?.Value;
this.assumeRoleService = assumeRoleService;
}
private IAmazonS3 Client { get; set; }
public async Task<IAmazonS3> GetClient(RegionEndpoint region)
{
if (this.Client != null && this.assumeRoleService.CredentialsExistAndAreValid())
{
return this.Client;
}
else
{
this.Client?.Dispose();
var credentials= await this.assumeRoleService.AssumeRoleAsync(this.config.AssumedRoleArn);
this.Client = new AmazonS3Client(assumeRoleCredentials, region);
return this.Client;
}
}
}
yes its thread safe
i asked same question from amazon's employee and his answer was yes
just enjoy :D
I'm trying to make use of IHttpClientFactory for the first time in a .NET Core 3.1 MVC project. I am using HttpClient to make calls to the Microsoft Graph API.
Whenever I need to make a call to the API I need to first check that the authentication token the application has is valid (i.e. hasn't expired) and, if not, use the refresh token to obtain a new authentication token.
Before learning of IHttpClientFactory I was initialising an instance of HttpClient for my application
and then whenever I needed to make an API call I'd first call my own PrepareHttpClient() function, so it'd go like this:
public class MyController : Controller
{
private static HttpClient httpClient = new HttpClient();
private readonly IOAuthService _oAuthService;
public MyController(IOAuthService oAuthService)
{
_oAuthService = oAuthService;
}
public async Task<IActionResult> Index()
{
var url = "http://whatever";
await PrepareHttpClient();
var response = await httpClient.GetAsync(url);
// Other stuff
}
private async Task PrepareHttpClient()
{
httpClient.DefaultRequestHeaders.Clear();
string bearerToken = await _oAuthService.GetOAuthBearerToken();
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {bearerToken}");
}
}
The GetOAuthBearerToken function is in a service which, as you can see, is injected into the controller and it basically does the dance of checking if the current token is expired and obtaining a new one if required, and it returns the valid token as a string. I don't think the full code of that function is necessary here but I can add it if required.
After learning about IHttpClientFactory and the fact that you can give an HttpClient a name and configure it in Startup.cs I thought "Great, I'll just call the GetOAuthBearerToken function when configuring the client". Something like
services.AddHttpClient("OneDrive", async(c) => {
string bearerToken = await IOAuthService.GetOAuthBearerToken();
c.DefaultRequestHeaders.Add("Authorization", $"Bearer {bearerToken}");
});
However, there are a number of problems with this:
Injecting a service in to the Startup class seems to be a no-go, which makes sense, so I can't inject my IOAuthService as I do in the controller.
The GetOAuthBearerToken itself uses HttpClient to obtain updated API tokens, and using DI to inject an IHttpClientFactory into a function which is itself injected into Startup sounds like... I'm losing my mind.
Ultimately I realised that none of that mattered because I need to check and refresh the API token every time I make a call, not just when the client is instantiated when the application starts. So it seems to me that the most elegant solution would be to override the CreateClient method in IHttpClientFactory, which I call right before making every API call, so that it could call my GetOAuthBearerToken function and then I'd know that every time I called CreateClient the HttpClient was good to go, with a valid bearer token in the header.
My question is (finally!), how would I go about overriding CreateClient in this way? Assuming that's the best way to achieve my aim.
Sorry, if someone could make this a comment.
The first thing I notice is you forget to clear the headers and add the new header for each request, for the instance you are registering in your startup.
Edit:
I'll just go ahead and answer it. Sorry I don't have any sort of c# compiler or intellisense.
public class MyController : Controller
{
private static IHttpClientFactory httpClientFactory = new IHttpClientFactory();
private readonly IOAuthService _oAuthService;
public MyController(IOAuthService oAuthService,IHttpClientFactory myfactory)
{
_oAuthService = oAuthService;
httpClientFactory = myfactory;
}
public async Task<IActionResult> Index()
{
var url = "http://whatever";
HttpClient myclient = httpClientFactory.CreateClient("MyNamedClient");
var response = await HttpClient.GetAsync(url);
// Other stuff
}
}
I am creating an application where SignalR is used to broadcast real-time tweets to a map. I am using the C# Tweetinvi library (tweetinvi.codeplex.com) to handle all of the logic associated with connecting to the Twitter Streaming API.
The Twitter API specifies that only one streaming connection can be open to Twitter at any time. As I am using SignalR, there is a dependency between the Streaming connection and the Hub class. I know that the Hub class is transient, meaning that it is created each time a client requests it, so I need to ensure that the instance of my Twitter Stream class injected into the Hub class is a singleton, or at least IFilteredStream is only created once in the lifetime of the application. Here is the boilerplate code to connect to the API:
public class TweetStream
{
private IFilteredStream _stream;
public TweetStream()
{
var consumerKey = ConfigurationManager.AppSettings.Get("twitter:ConsumerKey");
var consumerSecret = ConfigurationManager.AppSettings.Get("twitter:ConsumerSecret");
var accessKey = ConfigurationManager.AppSettings.Get("twitter:AccessKey");
var accessToken = ConfigurationManager.AppSettings.Get("twitter:AccessToken");
TwitterCredentials.SetCredentials(accessKey, accessToken, consumerKey, consumerSecret);
_stream = Stream.CreateFilteredStream();
}
// Return singular instance of _stream to Hub class for usage.
public IFilteredStream Instance
{
get { return _stream; }
}
}
The IFilteredStream interface exposes a lambda method as below which allows for receiving Tweets in real-time, which I would like to be able to access from within my SignalR Hub class:
_stream.MatchingTweetReceived += (sender, args) => {
Clients.All.broadcast(args.Tweet);
};
The source for this method can be found here
I've tried to implement Autofac, and it seems that the connection to the Twitter API happens, however nothing more happens. I've tried to debug this, but am unsure how to debug such a scenario using dependency injection. My Hub class currently looks like this:
public class TwitterHub : Hub
{
private readonly ILifetimeScope _scope;
private readonly TweetStream _stream;
// Inject lifetime scope and resolve reference to TweetStream
public TwitterHub(ILifetimeScope scope)
{
_scope = scope.BeginLifetimeScope();
_stream = scope.Resolve<TweetStream>();
var i = _stream.Instance;
_stream.MatchingTweetReceived += (sender, args) => {
Clients.All.broadcast(args.Tweet);
};
i.StartStreamMatchingAllConditions();
}
}
And finally, my OWIN Startup class, where I register my dependencies and Hub with Autofac:
[assembly: OwinStartup(typeof(TwitterMap2015.App_Start.OwinStartup))]
namespace TwitterMap2015.App_Start
{
public class OwinStartup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// use hubconfig, not globalhost
var hubConfig = new HubConfiguration {EnableDetailedErrors = true};
builder.RegisterHubs(Assembly.GetExecutingAssembly()); // register all SignalR hubs
builder.Register(i => new TweetStream()).SingleInstance(); // is this the correct way of injecting a singleton instance of TweetStream?
var container = builder.Build();
hubConfig.Resolver = new AutofacDependencyResolver(container);
app.MapSignalR("/signalr", hubConfig);
}
}
}
Sorry if this question is a bit of a mess, I'm having a hard time of understand what kind of architecture I need to implement to get this working! Open to advice / recommendations on how this could be improved, or how it should be done!
IMO this cannot work because you are wiring your event to call over the context of a specific hub instance, regardless of any code related to Autofac (which might have issues too but I'm not a big expert on it). Your hub's constructor will be called each time a new connection happens or a method is called from a client, so:
you are subscribing that event potentially multiple times per client. I don't know the Twitter API you are using, but on this note the fact that you call i.StartStreamMatchingAllConditions() all these times seems wrong to me
each time you create a closure over the Clients member of that instance in your event handler, which is supposed to go away when the hub is destroyed (so probably you are leaking memory)
What you need to do, given that your are calling over Client.All, and therefore this is a pure broadcast independent on any specific caller, is:
initialize your Twitter connection in the constructor of your TwitterStream service
in that same place (maybe with some indirection, but probably not necessary) take an instance of the hub context of your TwitterHub
subscribe to the event and use the context you just retrieved to broadcast over it
Such constructor might look like this:
public service TwitterStream : ??? <- an interface here?
{
...
public TwitterStream (ILifetimeScope scope ??? <- IMO you don't need this...)
{
//Autofac/Twitter stuff
...
var context = GlobalHost.DependencyResolver.GetHubContext<TwitterHub>();
_stream.MatchingTweetReceived += (sender, args) => {
context.Clients.All.broadcast(args.Tweet);
};
//maybe more Autofac/Twitter stuff
...
}
...
}
TwitterHub must exist, but in case you just need it to do this kind of broadcast to all, with no special code needed to monitor connections or handle client-generated calls, it could well be empty and it's just fine that your actual hub-related code lives outside of it and uses a IHubContext to broadcast messages. Such a code would take care of handling all the existing connected clients each time a tweet arrives, so no need to track them.
Of course if you have more requirements for actually handling clients separarely, then things might need to be different, but your code does not make me think otherwise.
In my Web API project, I have a dependency that requires the current request.
The code is below:
public interface IResourceLinker {
Uri Link(string routeName, object routeValues);
}
public class ResourceLinker : IResourceLinker {
private readonly HttpRequestMessage _request;
public ResourceLinker(HttpRequestMessage request) {
_request = request;
}
public Uri Link(string routeName, object routeValues) {
return new Uri(_request.GetUrlHelper()
.Link(routeName, routeValues));
}
}
public class TestController : ApiController {
private IResourceLinker _resourceLinker;
public TestController(IResourceLinker resourceLinker) {
_resourceLinker = resourceLinker
}
public Test Get() {
var url = _resourceLinker.Link("routeName", routeValues);
// etc.
}
}
Using Simple Injector, is it possible to inject the current request into the container at runtime ?
I tried the following :
public class InjectRequestHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
InjectRequest(request);
return base.SendAsync(request, cancellationToken);
}
public static void InjectCurrentRequestIntoContainer(
HttpRequestMessage request)
{
var resolver = (SimpleInjectorDependencyResolver)
request.GetDependencyScope();
resolver.Container.Register(() => request);
}
}
but recieved the following error
The container can't be changed after the first call to GetInstance, GetAllInstances and Verify.
Is there any way to inject the current request into the container at runtime?
The container blocks any registrations after the registration phase. This splits the usage of the container into two phases: registration and resolve. Simple Injector is not the only container that does this. Autofac for instance, does this even more explicitly by allowing users to make registrations using the ContainerBuilder that builds the container using the Build method as last step in the configuration phase.
Simple Injector disallows this mainly because making registrations later on during the application lifetime can easily lead to all sorts of problematic behavior such as race conditions. It also makes the DI configuration much harder to understand, since registrations are scattered throughout the application, while you should try to centralize registration when applying DI. As a side effect, this design allows the container have a linear performance characteristic in a multi-threading scenario, because the container's happy path is free of locks.
Simple Injector allows just-in-time registration using the ResolveUnregisteredType event, but this is not the way to go in your case.
The problem you are having is that you want to inject an object that is only known at runtime into the object graph. This might not be the best thing to do, since in general you should pass runtime dependencies through method arguments, while compile-time/configuration-time dependencies should be passed through the constructor.
But if you are certain that passing the HttpRequestMessage as a constructor argument is the right thing to do, what you need to do is to cache this message during the lifetime of the request and make a registration that allows to return that cached instance.
This is how this would look like:
// using SimpleInjector.Advanced; // for IsVerifying()
container.Register<HttpRequestMessage>(() =>
{
var context = HttpContext.Current;
if (context == null && container.IsVerifying())
return new HttpRequestMessage();
object message = context.Items["__message"];
return (HttpRequestMessage)message;
});
This registration will retrieve a HttpRequestMessage that is cached in the HttpContext.Items dictionary. There's an extra check to allow this registration to work during verification (when calling container.Verify()), since at that point in time there is no HttpContext.Current. But if you're not interest in verifying the container (which you usually really should btw), you can minimize it to this:
container.Register<HttpRequestMessage>(() =>
(HttpRequestMessage)HttpContext.Current.Items["__message"]);
With this registration, the only thing you have to do is cache an HttpRequestMessage instance before container.GetInstance is called to get the controller:
public static void InjectCurrentRequestIntoContainer(
HttpRequestMessage request)
{
HttpContext.Current.Items["__message"] = request;
}
UPDATE
The Web API Integration package contains a GetCurrentHttpRequestMessage extension method that allows you retrieving the HttpRequestMessage of the current request. The Web API Integration Wiki page describes how to use this extension method.