Having dynamic proxy with HttpClientFactory implementation - c#

I have Asp.Net Core WebApi. I am making Http requests according to HttpClientFactory pattern. Here is my sample code:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpClient<IMyInterface, MyService>();
...
}
public class MyService: IMyInterface
{
private readonly HttpClient _client;
public MyService(HttpClient client)
{
_client = client;
}
public async Task CallHttpEndpoint()
{
var request = new HttpRequestMessage(HttpMethod.Get, "www.customUrl.com");
var response = await _client.SendAsync(request);
...
}
}
I want to implement sending requests through dynamic proxy. This basically means that I might need to change proxy with each request. As for now I find out 2 approuces, non of which seems good to me:
1.Have a static proxy like this:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpClient<IMyInterface, MyService>().ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
Proxy = new WebProxy("http://127.0.0.1:8888"),
UseProxy = true
};
});
...
}
But I can only have single proxy per service in this approach.
2.Dispose HttpClient with each request:
HttpClientHandler handler = new HttpClientHandler()
{
Proxy = new WebProxy("http://127.0.0.1:8888"),
UseProxy = true,
};
using(var client = new HttpClient(handler))
{
var request = new HttpRequestMessage(HttpMethod.Get, "www.customUrl.com");
var response = await client.SendAsync(request);
...
}
But in this way I violate HttpClientFactory pattern and it might cause issues to application performance as stated in following article
Is there a third way where I could change proxy dinamically without re-creating HttpClient?

There is no way to change the any of the properties of HttpClientHandler or to assign a new version of HttpClientHandler to an existing HttpClient after it is instantiated. As such, it is then impossible to have a dynamic proxy for a particular HttpClient: you can only specify one proxy.
The correct way to achieve this is to use named clients, instead, and define a client for each proxy endpoint. Then, you'll need to inject IHttpClientFactory and pick one of the proxies to use, requesting the named client that implements that.
services.AddHttpClient("MyServiceProxy1").ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
Proxy = new WebProxy("http://127.0.0.1:8888"),
UseProxy = true
};
});
services.AddHttpClient("MyServiceProxy2").ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
Proxy = new WebProxy("http://127.0.0.1:8889"),
UseProxy = true
};
});
...
Then:
public class MyService : IMyInterface
{
private readonly HttpClient _client;
public MyService(IHttpClientFactory httpClientFactory)
{
_client = httpClientFactory.CreateClient("MyServiceProxy1");
}
public async Task CallHttpEndpoint()
{
var request = new HttpRequestMessage(HttpMethod.Get, "www.customUrl.com");
var response = await _client.SendAsync(request);
...
}
}

I can do that by inheriting from HttpClientHandler:
public class ProxyHttpHandler : HttpClientHandler
{
private int currentProxyIndex = 0;
private ProxyOptions proxyOptions;
public ProxyHttpHandler(IOptions<ProxyOptions> options)
{
proxyOptions = options != null ? options.Value : throw new ArgumentNullException(nameof(options));
UseProxy = true;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var proxy = proxyOptions.Proxies[currentProxyIndex];
var proxyResolver = new WebProxy(proxy.Host, proxy.Port)
{
Credentials = proxy.Credentials
};
Proxy = proxyResolver;
currentProxyIndex++;
if(currentProxyIndex >= proxyOptions.Proxies.Count)
currentProxyIndex = 0;
return base.SendAsync(request, cancellationToken);
}
}
Then I register my ProxyHttpHandler and ProxyOptions in IoC:
public IForksCoreConfigurationBuilder ConfigureProxy(Action<ProxyOptions> options)
{
Services.AddOptions<ProxyOptions>().Configure(options);
Services.AddTransient<ProxyHttpHandler>();
Services.AddHttpClient<IService, MyService>()
.ConfigurePrimaryHttpMessageHandler<ProxyHttpHandler>();
return this;
}

Related

.net services.AddHttpClient Automatic Access Token Handling

I am trying to write a Blazor app that uses client secret credentials to get an access token for the API. I wanted to encapsulate it in such a way that it handles the token fetching and refreshing behind the scenes. To achieve this, I created the following inherited class which uses IdentityModel Nuget package:
public class MPSHttpClient : HttpClient
{
private readonly IConfiguration Configuration;
private readonly TokenProvider Tokens;
private readonly ILogger Logger;
public MPSHttpClient(IConfiguration configuration, TokenProvider tokens, ILogger logger)
{
Configuration = configuration;
Tokens = tokens;
Logger = logger;
}
public async Task<bool> RefreshTokens()
{
if (Tokens.RefreshToken == null)
return false;
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync(Configuration["Settings:Authority"]);
if (disco.IsError) throw new Exception(disco.Error);
var result = await client.RequestRefreshTokenAsync(new RefreshTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = Configuration["Settings:ClientID"],
RefreshToken = Tokens.RefreshToken
});
Logger.LogInformation("Refresh Token Result {0}", result.IsError);
if (result.IsError)
{
Logger.LogError("Error: {0)", result.ErrorDescription);
return false;
}
Tokens.RefreshToken = result.RefreshToken;
Tokens.AccessToken = result.AccessToken;
Logger.LogInformation("Access Token: {0}", result.AccessToken);
Logger.LogInformation("Refresh Token: {0}" , result.RefreshToken);
return true;
}
public async Task<bool> CheckTokens()
{
if (await RefreshTokens())
return true;
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync(Configuration["Settings:Authority"]);
if (disco.IsError) throw new Exception(disco.Error);
var result = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = Configuration["Settings:ClientID"],
ClientSecret = Configuration["Settings:ClientSecret"]
});
if (result.IsError)
{
//Log("Error: " + result.Error);
return false;
}
Tokens.AccessToken = result.AccessToken;
Tokens.RefreshToken = result.RefreshToken;
return true;
}
public new async Task<HttpResponseMessage> GetAsync(string requestUri)
{
DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Tokens.AccessToken);
var response = await base.GetAsync(requestUri);
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
if (await CheckTokens())
{
DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Tokens.AccessToken);
response = await base.GetAsync(requestUri);
}
}
return response;
}
}
The idea is to keep from having to write a bunch of redundant code to try the API, then request/refresh the token if you are unauthorized. I tried it at first using extension methods to HttpClient, but there was no good way to inject the Configuration into a static class.
So my Service code is written as this:
public interface IEngineListService
{
Task<IEnumerable<EngineList>> GetEngineList();
}
public class EngineListService : IEngineListService
{
private readonly MPSHttpClient _httpClient;
public EngineListService(MPSHttpClient httpClient)
{
_httpClient = httpClient;
}
async Task<IEnumerable<EngineList>> IEngineListService.GetEngineList()
{
return await JsonSerializer.DeserializeAsync<IEnumerable<EngineList>>
(await _httpClient.GetStreamAsync($"api/EngineLists"), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
}
}
Everything compiles great. In my Startup, I have the following code:
services.AddScoped<TokenProvider>();
services.AddHttpClient<IEngineListService, EngineListService>(client =>
{
client.BaseAddress = new Uri(Configuration["Settings:ApiAddress"]);
});
Just to be complete, Token Provider looks like this:
public class TokenProvider
{
public string AccessToken { get; set; }
public string RefreshToken { get; set; }
}
When I run the App, it complains that it can't find a suitable constructor for EngineListService in the call to services.AddHttpClient. Is there a way to pass AddHttpClient an actual instance of the IEngineListService. Any other way I might be able to achieve this?
Thanks,
Jim
I think that EngineListService should not be registered as a HttpClient in services and instead you should register MPSHttpClient.
This follows the "Typed Client" example in the documentation and uses IHttpClientFactory behind the scenes.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests#typed-clients
When you use services.AddHttpClient the constructor needs a HttpClient parameter. That is how the HttpClientFactory initializes the HttpClient and then passes it into your service ready to go.
You can change your MPSHttpClient to not inherit HttpClient and instead add a HttpClient parameter to the constructor. You could also have it implement an interface like IMPSHttpClient
public class MPSHttpClient
{
public MPSHttpClient(HttpClient httpClient, IConfiguration configuration, TokenProvider tokens, ILogger logger)
{
HttpClient = httpClient;
Configuration = configuration;
Tokens = tokens;
Logger = logger;
}
}
You must remove these lines from MPSHttpClient and use the injected client.
// remove this
var client = new HttpClient();
In Startup add
services.AddHttpClient<MPSHttpClient>(client =>
{
// add any configuration
client.BaseAddress = new Uri(Configuration["Settings:ApiAddress"]);
});
Change EngineListService to a normal service registration as it is not a HttpClient
services.AddScoped<IEngineListService, EngineListService>()
Special thanks to #pinkfloydx33 for helping me solve this. This link that he shared https://blog.joaograssi.com/typed-httpclient-with-messagehandler-getting-accesstokens-from-identityserver/ was everything I needed. The trick was that there exists a class called DelegatingHandler that you can inherit and override the OnSendAsync method and do all of your token-checking there before sending it to the final HttpHandler. So my new MPSHttpClient class is as so:
public class MPSHttpClient : DelegatingHandler
{
private readonly IConfiguration Configuration;
private readonly TokenProvider Tokens;
private readonly ILogger<MPSHttpClient> Logger;
private readonly HttpClient client;
public MPSHttpClient(HttpClient httpClient, IConfiguration configuration, TokenProvider tokens, ILogger<MPSHttpClient> logger)
{
Configuration = configuration;
Tokens = tokens;
Logger = logger;
client = httpClient;
}
public async Task<bool> CheckTokens()
{
var disco = await client.GetDiscoveryDocumentAsync(Configuration["Settings:Authority"]);
if (disco.IsError) throw new Exception(disco.Error);
var result = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = Configuration["Settings:ClientID"],
ClientSecret = Configuration["Settings:ClientSecret"]
});
if (result.IsError)
{
//Log("Error: " + result.Error);
return false;
}
Tokens.AccessToken = result.AccessToken;
Tokens.RefreshToken = result.RefreshToken;
return true;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.SetBearerToken(Tokens.AccessToken);
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
if (await CheckTokens())
{
request.SetBearerToken(Tokens.AccessToken);
response = await base.SendAsync(request, cancellationToken);
}
}
return response;
}
}
The big changes here are the inheritance and I used DI to obtain the HttpClient much like #Rosco mentioned. I had tried to override OnGetAsync in my original version. When inheriting from DelegatingHandler, all you have to override is OnSendAsync. This will handle all of your get, put, post, and deletes from your HttpContext all in one method.
My EngineList Service is written as if there were no tokens to be considered, which was my original goal:
public interface IEngineListService
{
Task<IEnumerable<EngineList>> GetEngineList();
}
public class EngineListService : IEngineListService
{
private readonly HttpClient _httpClient;
public EngineListService(HttpClient httpClient)
{
_httpClient = httpClient;
}
async Task<IEnumerable<EngineList>> IEngineListService.GetEngineList()
{
return await JsonSerializer.DeserializeAsync<IEnumerable<EngineList>>
(await _httpClient.GetStreamAsync($"api/EngineLists"), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
}
}
The Token Provider stayed the same. I plan to add expirations and such to it, but it works as is:
public class TokenProvider
{
public string AccessToken { get; set; }
public string RefreshToken { get; set; }
}
The ConfigureServices code changed just a bit:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<TokenProvider>();
services.AddTransient<MPSHttpClient>();
services.AddHttpClient<IEngineListService, EngineListService>(client =>
{
client.BaseAddress = new Uri(Configuration["Settings:ApiAddress"]);
}).AddHttpMessageHandler<MPSHttpClient>();
...
}
You instantiate MPSHttpClient as Transient, then reference it with the AddHttpMessageHandler call attached to the AddHttpClient call. I know this is different than how others implement HttpClients, but I learned this method of creating client services from a Pluralsight video and have been using it for everything. I create a separate Service for each entity in the database. If say I wanted to do tires, I would add the following to ConfigureServices:
services.AddHttpClient<ITireListService, TireListService>(client =>
{
client.BaseAddress = new Uri(Configuration["Settings:ApiAddress"]);
}).AddHttpMessageHandler<MPSHttpClient>();
It will use the same DelegatingHandler so I can just keep adding services for each entity type while no longer worrying about tokens. Thanks to everyone that responded.
Thanks,
Jim

Get name HttpClient from IHttpClientFactory injected with DI

In Blazor I have setup two HttpClients. One for my API and one for MS Graph API.
The Graph API is new, and have forced me to find a way to inject a named httpclient in to my services.
This is all the code in Main
public class Program
{
public static async Task Main(string[] args)
{
var b = WebAssemblyHostBuilder.CreateDefault(args);
b.RootComponents.Add<App>("app");
var samsonApiUrl = new Uri(b.HostEnvironment.BaseAddress + "api/");
b.Services.AddHttpClient("SamsonApi",client =>
{
client.BaseAddress = samsonApiUrl;
// add jwt token to header
// add user agent to header
}).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
b.Services.AddTransient<GraphCustomAuthorizationMessageHandler>();
b.Services.AddHttpClient<GraphHttpClientService>("GraphAPI",
client => client.BaseAddress = new Uri("https://graph.microsoft.com/"))
.AddHttpMessageHandler<GraphCustomAuthorizationMessageHandler>();
b.Services.AddScoped(provider => provider.GetService<IHttpClientFactory>().CreateClient("SamsonApi"));
b.Services.AddScoped(provider => provider.GetService<IHttpClientFactory>().CreateClient("GraphAPI"));
b.Services.AddMsalAuthentication<RemoteAuthenticationState, CustomUserAccount>(options =>
{
b.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("1c8d4e31-97dd-4a54-8c2b-0d81e4356bf9/API.Access");
options.UserOptions.RoleClaim = "role";
}).AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount, CustomUserFactory>();
// add Radzen services
b.Services.AddScoped<DialogService>();
b.Services.AddScoped<NotificationService>();
b.Services.AddScoped<TooltipService>();
// add samson component services
b.Services.AddSingleton<FormTitleState>();
// Add Http Services
b.Services.Scan(scan =>
{
scan.FromAssemblyOf<ICustomerService>()
.AddClasses(classes => classes.Where(type => type.Name.EndsWith("Service")))
.AsMatchingInterface()
.WithScopedLifetime();
});
await b.Build().RunAsync();
}
}
This is the code that has to change.
It's scan all my service and get a HttpClient injected.
And since I now have two I get a random client injected.
How can I inject a named client into all of my services? I can handle the graph API service as a special case.
b.Services.Scan(scan =>
{
scan.FromAssemblyOf<ICustomerService>()
.AddClasses(classes => classes.Where(type => type.Name.EndsWith("Service")))
.AsMatchingInterface()
.WithScopedLifetime();
});
Example of a service calling my API
public class ActiveAgreementService : IActiveAgreementService
{
private readonly HttpClient _client;
public ActiveAgreementService(HttpClient client)
{
_client = client;
}
public async Task<List<ActiveAgreementDto>> GetActiveAgreements()
{
var lst = await _client.GetFromJsonAsync<ActiveAgreementDto[]>("ActiveAgreement");
return lst.ToList();
}
}
Okay ended up with replacing HttpClient with IHttpClientFactory in all my services
public UserService(IHttpClientFactory clientFactory)
{
_client = clientFactory.CreateClient("SamsonApi");
}
I assume you're using ASP.NET Core, although it's not clear which dependency injection framework you're using.
In that case, you could have your classes depend on IHttpClientFactory and then setup the configuration with named clients:
// Named client like you're currently doing
b.Services.AddHttpClient("SamsonApi", client =>
{
client.BaseAddress = samsonApiUrl;
// add jwt token to header
// add user agent to header
}).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
//...
b.Services.AddHttpClient("GraphAPI", client =>
client.BaseAddress = new Uri("https://graph.microsoft.com/"))
.AddHttpMessageHandler<GraphCustomAuthorizationMessageHandler>();
// And in your dependent class
public class ActiveAgreementService : IActiveAgreementService
{
private readonly HttpClient _client;
public ActiveAgreementService(IHttpClientFactory clientFac)
{
// Whichever one you need:
_client = clientFac.CreateClient("SamsonApi");
_client = clientFac.CreateClient("GraphAPI");
}
public async Task<List<ActiveAgreementDto>> GetActiveAgreements()
{
var lst = await _client.GetFromJsonAsync<ActiveAgreementDto[]>("ActiveAgreement");
return lst.ToList();
}
}
... or with typed clients you specify the instance for each class that depends on it:
// This HttpClient is only injected into ActiveAgreementService
b.Services.AddHttpClient<ActiveAgreementService>(client =>
{
client.BaseAddress = samsonApiUrl;
// add jwt token to header
// add user agent to header
}).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
//...
// This HttpClient is only injected into GraphHttpClientService
b.Services.AddHttpClient<GraphHttpClientService>(client =>
client.BaseAddress = new Uri("https://graph.microsoft.com/"))
.AddHttpMessageHandler<GraphCustomAuthorizationMessageHandler>();
// And in your dependent class
public class ActiveAgreementService : IActiveAgreementService
{
private readonly HttpClient _client;
public ActiveAgreementService(HttpClient client)
{
_client = client;
}
public async Task<List<ActiveAgreementDto>> GetActiveAgreements()
{
var lst = await _client.GetFromJsonAsync<ActiveAgreementDto[]>("ActiveAgreement");
return lst.ToList();
}
}

Mocking Fails for Azure Function (IHttpClientFactory)

I am facing issues while mocking the IHttpClientFactory Interface in Azure Function. Here is What am doing, I have trigger, once the message the received I am calling an API to update data. I am using SendAsync Method for that. While writing unit test case, I am not able to mock the client.For testing , I have tried making the get call in the constructor itself, still didn't work.
Function Class
public class UpdateDB
{
private readonly IHttpClientFactory _clientFactory;
private readonly HttpClient _client;
public UpdateDB(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
_client = clientFactory.CreateClient();
_client.GetAsync("");
}
[FunctionName("DB Update")]
public async Task Run([ServiceBusTrigger("topic", "dbupdate", Connection = "connection")]string mySbMsg, ILogger log)
{
var client = _clientFactory.CreateClient();
log.LogInformation($"C# ServiceBus topic trigger function processed message: {mySbMsg}");
DBConvert payload = JsonConvert.DeserializeObject<DBConvert>(mySbMsg);
string jsonContent = JsonConvert.SerializeObject(payload);
var httpContent = new StringContent(jsonContent, Encoding.UTF8, "application/json");
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, "api/DBU/data");
message.Content = httpContent;
var response = await client.SendAsync(message);
}
}
TestClass
namespace XUnitTestProject1
{
public class DelegatingHandlerStub : DelegatingHandler
{
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
public DelegatingHandlerStub()
{
_handlerFunc = (request, cancellationToken) => Task.FromResult(request.CreateResponse(HttpStatusCode.OK));
}
public DelegatingHandlerStub(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handlerFunc)
{
_handlerFunc = handlerFunc;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return _handlerFunc(request, cancellationToken);
}
}
public class test
{
[Fact]
public async Task Should_Return_Ok()
{
//
Mock<ILogger> _logger = new Mock<ILogger>();
var expected = "Hello World";
var mockFactory = new Mock<IHttpClientFactory>();
var configuration = new HttpConfiguration();
var clientHandlerStub = new DelegatingHandlerStub((request, cancellationToken) =>
{
request.SetConfiguration(configuration);
var response = request.CreateResponse(HttpStatusCode.Accepted);
return Task.FromResult(response);
});
var client = new HttpClient(clientHandlerStub);
mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(client);
var clientTest = mockFactory.Object.CreateClient();
//Works Here, but not in the instance.
clientTest.GetAsync("");
IHttpClientFactory factory = mockFactory.Object;
var service = new UpdateDB(factory);
await service.Run("", _logger.Object);
}
}
}
I have followed the sample here. How to mock the new HttpClientFactory in .NET Core 2.1 using Moq
For mocking/intercepting HttpClient usage, I would recommend you to use mockhttp
Your test would then be:
class Test {
private readonly Mock<IHttpClientFactory> httpClientFactory;
private readonly MockHttpMessageHandler handler;
constructor(){
this.handler = new MockHttpMessageHandler();
this.httpClientFactory = new Mock<IHttpClientFactory>();
this.httpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>()))
.Returns(handler.ToHttpClient());
}
[Fact]
public async Task Test(){
// Arrange
this.handler.Expect("api/DBU/data")
.Respond(HttpStatusCode.Ok);
var sut = this.CreateSut();
// Act
await sut.Run(...);
// Assert
this.handler.VerifyNoOutstandingExpectation();
}
private UpdateDB CreateSut() => new UpdateDB(this.httpClientFactory.Object);
}
You can further configure how the HTTP request expectation should behave, but for that you should read a bit the documentation of mockhttp

.NET Core HttpClient - avoid auto redirect?

I'm using .NET Core 2.1 to consume some APIs. In my Startup.cs I configure a named HttpClient like this:
services.AddHttpClient("MyApi", client =>
{
client.BaseAddress = new Uri("https://foo.com");
});
I would like to disable the automatic redirect following. I know you can do that when creating a new HttpClient by passing in a SocketsHttpHandler with AllowAutoRedirect = false. But when using the above factory pattern, I'm not seeing where I can configure this as I don't have access to the HttpClient's construction.
The AllowAutoRedirect property belongs to the HttpMessageHandler. When using the AddHttpClient approach, you can configure the HttpMessageHandler itself using ConfigurePrimaryHttpMessageHandler. Here's an example of how to use this:
services
.AddHttpClient("MyApi", client =>
{
client.BaseAddress = new Uri("https://foo.com");
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
AllowAutoRedirect = false
};
});
This is covered in the official docs: Configure the HttpMessageHandler
Like this you can configure httpClient as you wish
public interface ITwitterApiClient
{
Task<List<string>> GetTweets();
}
public class TwitterApiClient : ITwitterApiClient
{
public async Task<List<string>> GetTweets()
{
using (HttpClient client = new HttpClient())
{
//Blah blah do everything here I want to do.
//var result = await client.GetAsync("/tweets");
return new List<string> { "Tweet tweet" };
}
}
}
In service definition
services.AddTransient<ITwitterApiClient, TwitterApiClient>();
In controller
[Route("api/[controller]")]
public class TweetController : Controller
{
private readonly ITwitterApiClient _twitterApiClient;
public TweetController(ITwitterApiClient twitterApiClient)
{
_twitterApiClient = twitterApiClient;
}
[HttpGet]
public async Task<IEnumerable<string>> Get()
{
return await _twitterApiClient.GetTweets();
}
}
From here

How can I use proxies for web requests in Flurl?

I have a simple post request using the Flurl client, and I was wondering how to make this request using a proxy using information like the IP, port, username, and password.
string result = await atc.Request(url)
.WithHeader("Accept", "application/json")
.WithHeader("Content-Type", "application/x-www-form-urlencoded")
.WithHeader("Host", "www.website.com")
.WithHeader("Origin", "http://www.website.com")
.PostUrlEncodedAsync(new { st = colorID, s = sizeID, qty = 1 })
.ReceiveString();
I was looking for a similar answer and found this:
https://github.com/tmenier/Flurl/issues/228
Here is a copy of the contents of that link. It worked for me!
You can do this with a custom factory:
using Flurl.Http.Configuration;
public class ProxyHttpClientFactory : DefaultHttpClientFactory {
private string _address;
public ProxyHttpClientFactory(string address) {
_address = address;
}
public override HttpMessageHandler CreateMessageHandler() {
return new HttpClientHandler {
Proxy = new WebProxy(_address),
UseProxy = true
};
}
}
To register it globally on startup:
FlurlHttp.Configure(settings => {
settings.HttpClientFactory = new ProxyHttpClientFactory("http://myproxyserver");
});

Categories