C# Performant method caller attribute usage - c#

I am writing a client library for an API that provides both public and authenticated endpoints. I would like to easily denote which endpoints require authentication using attributes. For instance:
public async Task<ApiResponse> GetPublicData()
{
var request = CreateRequest( "v1/public" );
return await _httpClient.GetAsync( request );
}
[RequiresAuthentication]
public async Task<ApiResponse> GetPrivateData()
{
var request = CreateRequest( "v1/private" );
return await _httpClient.GetAsync( request );
}
private ApiRequest CreateRequest( string endpoint )
{
var request = new ApiRequest( endpoint );
// if (caller has RequiresAuthenticationAttribute)
// SignRequest( request, _credentials );
return request;
}
As far as I am aware, the only way I can access whether or not the caller of CreateRequest has a RequiresAuthenticationAttribute is to create a stack frame, find the method via reflection, and then attempt to get the attribute from the MethodInfo. This can be incredibly slow.
Is there any other way to pass this information into the callee that won't kill performance. I know that the requests will always be limited by the response time of the API, but given that it has to do with financial data, being able to fire off requests as soon as possible is a must, and being able to do it in a clean way that involves attributes instead of manually passing parameters would be very nice.

You could try using the CallerMemberNameAttribute class.
The attributes "Allows you to obtain the method or property name of the caller to the method."
private ApiRequest CreateRequest(string endpoint, [CallerMemberName] string callerMemberName= "")
{
var methodInfo = this.GetType().GetMethod(callerMemberName);
var attributes = (RequiresAuthenticationAttribute)method.GetCustomAttributes(typeof(RequiresAuthenticationAttribute), true);
var request = new ApiRequest( endpoint );
if (attributes.Any())
SignRequest(request, _credentials);
return request;
}

If you are set on using attributes, then you are going to have to use Reflection in one way or another. Some reflection mechanisms are faster than others, but there is still a runtime penalty which you will have to pay. On the other hand, if what you want is a separation of concerns (and using attributes is not a given), then you might want to think about using interfaces to separate those concerns.
For example:
public interface IAuthenticated
{
public async Task<ApiResponse> GetPrivateData();
}
public interface IPublicAccess
{
public async Task<ApiResponse> GetPublicData();
}
public async Task<ApiResponse> IPublicAccess.GetPublicData()
{
var request = CreateRequest( "v1/public" );
return await _httpClient.GetAsync( request );
}
public async Task<ApiResponse> IAuthenticated.GetPrivateData()
{
var request = CreateRequest( "v1/private" );
return await _httpClient.GetAsync( request );
}
private ApiRequest CreateRequest( string endpoint )
{
var request = new ApiRequest( endpoint );
// if (caller has RequiresAuthenticationAttribute)
// SignRequest( request, _credentials );
return request;
}

Related

Intercept HttpClient with third party extensions using state

Injecting state into your HttpRequest when using IHttpClientFactory is achievable by populating HttpRequestMessage.Properties see Using DelegatingHandler with custom data on HttpClient
Now if I have third party extensions on HttpClient (such as IdentityModel), how would I intercept these http requests using custom state?
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
var httpClient = factory.CreateClient();
// GetDiscoveryDocumentAsync is a third party extension method on HttpClient
// I therefore cannot inject or alter the request message to be handled by the InterceptorHandler
var discovery = await httpClient.GetDiscoveryDocumentAsync();
// I want id to be associated with any request / response GetDiscoveryDocumentAsync is making
}
The only plausible solution I currently have is to override HttpClient.
public class InspectorHttpClient: HttpClient
{
private readonly HttpClient _internal;
private readonly int _id;
public const string Key = "insepctor";
public InspectorHttpClient(HttpClient #internal, int id)
{
_internal = #internal;
_id = id;
}
public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// attach data into HttpRequestMessage for the delegate handler
request.Properties.Add(Key, _id);
return _internal.SendAsync(request, cancellationToken);
}
// override all methods forwarding to _internal
}
A then I'm able to intercept these requests.
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
var httpClient = new InspectorHttpClient(factory.CreateClient(), id);
var discovery = await httpClient.GetDiscoveryDocumentAsync();
}
Is that a plausible solution? Something tell me now not to override HttpClient. Quoting from https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-5.0
The HttpClient also acts as a base class for more specific HTTP clients. An example would be a FacebookHttpClient providing additional methods specific to a Facebook web service (a GetFriends method, for instance). Derived classes should not override the virtual methods on the class. Instead, use a constructor overload that accepts HttpMessageHandler to configure any pre- or post-request processing instead.
I almost included this in my other answer as an alternative solution, but I figured it was too long already. :)
The technique is practically the same, but instead of HttpRequestMessage.Properties, use AsyncLocal<T>. "Async local" is kind of like thread-local storage but for a specific asynchronous code block.
There are a few caveats to using AsyncLocal<T> that aren't particularly well-documented:
Use an immutable nullable type for T.
When setting the async local value, return an IDisposable that resets it.
If you don't do this, then only set the async local value from an async method.
You don't have to follow these guidelines, but they will make your life much easier.
With that out of the way, the solution is similar to the last one, except it just uses AsyncLocal<T> instead. Starting with the helper methods:
public static class AmbientContext
{
public static IDisposable SetId(int id)
{
var oldValue = AmbientId.Value;
AmbientId.Value = id;
// The following line uses Nito.Disposables; feel free to write your own.
return Disposable.Create(() => AmbientId.Value = oldValue);
}
public static int? TryGetId() => AmbientId.Value;
private static readonly AsyncLocal<int?> AmbientId = new AsyncLocal<int?>();
}
Then the calling code is updated to set the ambient value:
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
using (AmbientContext.SetId(id))
{
var httpClient = factory.CreateClient();
var discovery = await httpClient.GetDiscoveryDocumentAsync();
}
}
Note that there is an explicit scope for that ambient id value. Any code within that scope can get the id by calling AmbientContext.TryGetId. Using this pattern ensures that this is true for any code: synchronous, async, ConfigureAwait(false), whatever - all code within that scope can get the id value. Including your custom handler:
public class HttpClientInterceptor : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var id = AmbientContext.TryGetId();
if (id == null)
throw new InvalidOperationException("The caller must set an ambient id.");
// associate the id with this request
Database.InsertEnquiry(id.Value, request);
return await base.SendAsync(request, cancellationToken);
}
}
Followup readings:
Blog post on "async local" - written before AsyncLocal<T> existed, but has details on how it works. This answers the questions "why should T be immutable?" and "if I don't use IDisposable, why do I have to set the value from an async method?".

Why is HttpContext.Session status changing to disposed?

I'm trying to store token I get from external api on session.
code snippet concerning this;
[HttpPost]
public async void Post()
{
if (HttpContext.Session.GetValue<User>("Token") == null)
{
HttpContext.Session.SetValue("Token", "test");
var res = await _loginBusiness.GetToken();
HttpContext.Session.SetValue("Token", res);
}
}
HttpContext.Session.SetValue("Token", "test");
in this part, it doesn't occur any error but second the same code line give an error after GetToken().
related error
System.ObjectDisposedException: 'IFeatureCollection has been disposed.
Object name: 'Collection'.'
Also GetToken():
public async Task<User> GetToken()
{
String url = "login/login";
var client = httpClientFactory.CreateClient("VoiceScope");
var postRes = await client.PostAsync<User>(new UserLogin(), url);
return postRes;
}
The problem is that you are using async void. These promises can't be observed and their semantics end up a lot different from a normal Task. Your disposal is happening early because the infrastructure just assumes your Post method has completed (it has no way to tell otherwise).
Change the signature of Post to be:
public async Task Post()
Please note that async void should be limited to event handlers.
I am not sure about using HttpContext. You have IHttpContextAccessor in asp.net core.
I think for store token you can use this
public class UserContext
{
public UserContext(IHttpContextAccessor context)
{
Token = GetAccessToken(context);
}
private static string GetAccessToken(IHttpContextAccessor contextAccessor)
{
var identity = (ClaimsIdentity)contextAccessor?.HttpContext?.User?.Identity;
return identity?.Claims.FirstOrDefault(x => x.Type == "token")?.Value;
}
public string Token { get; }
}
And then, add this staff in your DI like scope object and use it in controllers via ServiceProvider.

Multiple httpclient instances with same implementation in dotnet core

I would like to call a third party API which provided us two different authorization token values. So we are getting two different sets of results back via invoking the same endpoint URL.
EndpointUrl: https://mail.yahoo.com/
Authorization: Token User123
//Do something with the response for User123
Authorization: Token User345
//Do something with the response for User345
In my client service, my wrapper function should invoke this API by calling it twice with different token values. Get the result and merge it.
Here is my service.
public class MailService : IMailService
{
private readonly HttpClient _httpClient;
public MailService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<UserResponse> GetUserResponse()
{
var uri = new Uri(_httpClient.BaseAddress.AbsoluteUri + "/user-data/");
var response = await _httpClient.GetAsync(uri);
return response;
}
}
I was using Typed Client:
services.AddHttpClient<IMailService,MailService>(client =>
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", "User123");
client.BaseAddress = new Uri("https://mail.yahoo.com/");
})
Problem Statement:
What is the best way to retrieve the results? I am not sure if I should create two separate instances of HttpClient? I would like to avoid repetitive code if I go with two different classes containing their own HttpClient.
Maybe my solution lies somewhere in Named Client. I just don't know how to implement that gracefully.
Any help would be appreciated.
You can delegate adding the token header later for each message. Remove the auth header from Startup and add Http message handler (Create a new class "AuthHandler").
builder.Services.AddScoped<AuthHandler>();
services.AddHttpClient<IMailService,MailService>(client => {
client.BaseAddress = new Uri("https://mail.yahoo.com/");
})
.AddHttpMessageHandler<AuthHandler>();
In the AuthHandler, you can add the logic to retrieve and set the auth header.
The override SendAsync method will be called every time a http call is made. Below is a sample code, you can modify as per your logic/requirements:
public class AuthHandler : DelegatingHandler
{
private readonly AppSettings _appSettings;
private static string? _accessToken;
public AuthHandler(IOptions<AppSettings> options)
{
_appSettings = options.Value;
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var token = GetToken();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
return base.SendAsync(request, cancellationToken);
}
private string GetToken()
{
if (_accessToken != null)
{
return _accessToken;
}
_accessToken = "123";// Get your token
return _accessToken;
}
}
What is the best way to retrieve the results?
Named clients are one solution, if you are certain you will always have two authentication headers. That seems very odd to me, though.
SendAsync is another solution. By calling SendAsync instead of GetAsync, you can provide an HttpRequestMessage that can have custom headers. GetAsync is essentially a wrapper around SendAsync for convenience, and you can use the lower-level SendAsync since you need more customization.
I am going to answer, and this is more-or-less opinion based. I like my DI services to be decoupled from everything else in my project. Basically you are putting a configuration to another service in your startup.cs. I like to keep all that stuff in the service that consumes the HttpClient.
So when I inject the IHttpClientFactory, I do it by simply calling:
services.AddHttpClient();
And move on.
Now, in your MailService, you would inject it as so:
public class MailService : IMailService
{
// or store this in your applications external configuration
const Uri BaseUri = new Uri("https://mail.yahoo.com/");
const string UserDataPath = "/user-data/";
private readonly IHttpClientFactory _httpClientFactory;
public MailService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<UserResponse> GetUserResponse(string token)
{
var client = _httpClientFactory.CreateClient();
var uri = new UriBuilder(BaseUri){ Path = UserDataPath }.Uri;
using (var msg = new HttpRequestMessage(HttpMethod.Get, uri))
{
msg.Headers.Authorization = new AuthenticationHeaderValue("Token", token);
using (var resp = await _client.SendAsync(msg).ConfigureAwait(false))
{
resp.EnsureSuccessStatusCode();
return await resp.Content.ReadAsAsync<UserResponse>().ConfigureAwait(false);
}
}
}
}
Keep in mind that you should use the HttpRequestMessage pattern and not modify any Default... properties of an HttpClient. It could be in use some where else in your application at that time.
Using HttpRequestMessage ensures you get a fresh set of headers that no one else can modify. To sum it up: HttpClient.Headers are not thread-safe.
ETA:
Now that I think about it... that really is your question. You want to use two different headers using one HttpClient. It won't work in the scenario you presented. You would have to have two HttpClient with their own distinct headers.
So, if you don't want to do it the old-school way I presented, you should consider "Named" clients... each configuration has a different name.

Casting simple generic objects

I have a method that I created for simplifying using HttpClient calls. it uses the method HttpReponse.Content.ReadAsAsync().Result to get the response from the API.
This all works fine. My method looks something like this:
public static T ExecuteAPIGetRequest<T>(string url, Dictionary<string, string> parameters)
{
HttpClient client = new HttpClient();
//basic authentication
var t = new object();
string baseURL = "myurl";
//Execute request
HttpResponseMessage response = client.GetAsync(baseURL).Result;
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return (T)t;
}
}
My question is, if the query fails it needs to return an empty type of T. This is fine if its a custom class I have written, but it does not work for objects like string or string[]. Any ideas?
Cheers
NCBL
try to return default(T)
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return default(T);
}
default will return null for reference types, and zeros numeric values int, double, etc.. and corresponding default values for custom struct and enum.
Daniel kindly noted one issue: if for reference types you want to return default object and not null, you should define generic constraint new T(). Now you can instantiate object of type T using call to parameter-less constructor. Full method is given below:
public static T ExecuteAPIGetRequest<T>(string url,
Dictionary<string, string> parameters)
where T : new()
{
HttpClient client = new HttpClient();
//basic authentication
string baseURL = "myurl";
HttpResponseMessage response = client.GetAsync(baseURL).Result;
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return new T(); //returns an instance, not null
}
}
Now you will return default object for reference types, not null. Open type T can only take types, which have constructor by default (without parameters)
Can I make a suggestion that you consider an approach like this....
class Program
{
static void Main(string[] args)
{
var client = new HttpClient();
//basic authentication
string baseURL = "myurl";
var link = Link<Foo>.Create(baseURL);
var response = client.SendAsync(link.CreateRequest()).Result;
var myfoo = link.ProcessResponse(response);
}
}
public class Link<T>
{
public Uri Target { get; set; }
public static Link<T> Create(string url)
{
return new Link<T>() {Target = new Uri(url)};
}
public HttpRequestMessage CreateRequest()
{
return new HttpRequestMessage() {RequestUri = Target};
}
public T ProcessResponse(HttpResponseMessage response)
{
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return new T(); //returns an instance, not null
}
}
}
public class Foo
{
}
By encapsulating the mechanics of creating the link into a static factory method and the handling of the response into the ProcessResponse method you get a similar level of re-usability, but you also get the benefits of reusing the same HttpClient. This allows you to actually take advantage of the DefaultRequestHeaders and it will stop HttpClient from keep closing the connection when it gets disposed.
Also, by avoiding wrapping the Async call inside a sync method, you can let the calling code decide whether to block for the result or handle it asynchronously. You are likely to run into deadlock issues at some point with the way you are currently using .Result.
This technique of creating a Link class to encapsulate specific behaviour relating to de-referencing a URL can be extremely useful. It is easy to add parameters that can be used to fill in URI templates. You can also use it to handle request bodies.

How to generalize login, action, and logout when using Web API HttpClient

I am using HttpClient (aka Web API client) to consume RESTfull services.
Services require session to be established (via login) and then destroyed (via logout) upon each operation. So the call to consume service A looks something like this (pseudocode)
// setup
create auth dictionary authDict
create authenticationContent using FormUrlEndodeContent(authDict)
create cookieContainer
create HttpClientHandler...
create HttpClient
// login
await httpClient.PostAsync(LoginUrl, authenticationContent);
do error checking
// perform Operation A
await httpClient.....post...or...get...
extract data, process it, tranform it, get a cup of coffee, etc, etc
populate OperationAResult
// logout
await httpClient.GetAsync(LogoutUrl);
// return result
return OperationAResult
My question is, how can I easily reuse setup, login, and logout for different operations?
Should I be creating some method that will take in Action<> and if so how do I make sure that operations occur in order?
Probably the easiest way is to just write a wrapper class.
public class MyHttpClient
{
private HttpClient _client = new HttpClient();
private MyHttpClientSetup _setup;
public MyHttpClient(MyHttpClientSetup setup)
{
this._setup = setup;
}
private void HttpLogin()
{
// .. custom login stuff that uses this._setup
}
private void HttpLogout()
{
// .. custom logout stuff that uses this._setup
}
public void Reset()
{
this._client = new HttpClient();
}
// Wrapped Properties from the private HttpClient (1 example)
public Uri BaseAddress
{
get{ return this._client.BaseAddress;}
set{ this._client.BaseAddress = value;}
}
// Wrapped HttpMethods (1 example)
// Extremely poorly written, should be delegated properly
// This is just a bad example not using Task properly
public Task<HttpResponseMessage> DeleteAsync(string requestUri)
{
this.HttpLogin();
Task<HttpResponseMessage> result = this._client.DeleteAsync(requestUri);
this.HttpLogout();
return result;
}
public class MyHttpClientSetup
{
// Properties required for setup;
}
}
You may be able to create a new MessageHandler to handle this stuff for you transparently.
public class ConnectionHandler : DelegatingHandler {
public HttpClient HttpClient {get;set;}
public TestHandler(HttpMessageHandler handler) {
this.InnerHandler = handler;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
// Do your login stuff here
return base.SendAsync(request, cancellationToken) // Make your actual request
.ContinueWith(t => {
// Do your logout stuff here
}
}
}
Then you can just use a single instance of a HttpClient to do all your requests. To add your handler to the request/response pipeline you just need to create a regular HttpClientHandler, assign it to the InnerHandler property of your DelegatingHandler and then pass your new handler into the constructor of the HttpClient. From that point on, all requests made via the HttpClient will be routed through your ConnnectionHandler.
var connectionHandler = new ConnectionHandler(new HttpClientHandler());
var client = new HttpClient(connectionHandler);
connectionHandler.HttpClient = client;
var response = client.GetAsync("http://example.org/request").Result;
The advantage of using a single HttpClient instance is that you don't have to keep re-specifying the DefaultRequestHeaders. Also, disposing the HttpClient will kill the TCP Connection so the next request will have to re-open it.

Categories