In Angular there is HttpInterceptor which make it really nice to intercept HttpClient call on the app, also handle errors of the request.
Is there a C#/.NET library that do the same thing?
HttpClientHandler
You have to override SendAsync
internal class MyHttpClientHandler : HttpClientHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Do before call
var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
// do after call
return response;
}
}
var handler = new MyHttpClientHandler();
var client = new HttpClient(handler)
{
BaseAddress = new Uri("my-api-uri")
};
You are looking for what is called Middelware in the ASP.net world. These are code that are placed in an http pipeline that can handle incoming requests and outgoing responses.
Have a look at this article that explains how it works and what you can do.
Related
For my application i need to make a named client for HttpRequests. I can create a named client in Startup. And to access it i inject an "IHttpClientFactory" and create a client from that. But the client needs to have an access token as an authorization header, and i cannot create the token in Startup. Therefor i need a way to create a named client outside of the Startup class. i have already tried injecting "IServiceCollection" into a controller. But this does not work.
Or is there maybe a way to edit a named client after it is already created in startup?
A similar solution to the one posted by #Ruben-J is to create a custom HttpMessageHandler which assigns an authorization header to requests made through the HttpClient at request-time.
You can create a custom HttpMessageHandler that can be assigned to a named HttpClient in Startup like so:
public class YourHttpMessageHandler : DelegatingHandler
{
private readonly IYourTokenProviderService _yourTokenProviderService;
public YourHttpMessageHandler(IYourTokenProviderService yourTokenProviderService)
: base()
{
_yourTokenProviderService = yourTokenProviderService;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = SendAsyncWithAuthToken(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
await _yourTokenProviderService.RefreshTokenAsync();
response = SendAsyncWithAuthToken(request, cancellationToken);
}
return response;
}
private async Task<HttpResponseMessage> SendWithAuthTokenAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _yourTokenProviderService.Token);
return await base.SendAsync(request, cancellationToken);
}
}
You then configure your services and named HttpClient in Startup:
public virtual void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<IYourTokenProviderService, YourTokenProviderService>();
services.AddTransient<YourHttpMessageHandler>();
services.AddHttpClient<IYourNamedHttpClient, YourNamedHttpClient>()
.AddHttpMessageHandler<YourHttpMessageHandler>();
...
}
Its worth noting that the current implementation of Polly's AddPolicyHandler is also adding its own DelegatingHandler.
For more background see the Microsoft documentation on adding DelegatingHandler's. Here is also great series of articles from Steve Gordon.
You could use Polly to add a policy handler to your client. You can then add logic if a request returns a 401 Unauthorized. So for example get your service that uses the client to refresh a bearer token and also set it for the current request. This is just a quick solution and maybe there are more elegant solutions. But this will also come in handy if your token expires. Cause then it will be refreshed automatically.
services.AddHttpClient("YourClient")
.AddPolicyHandler((provider, request) =>
{
return Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.Unauthorized)
.RetryAsync(1, async (response, retryCount, context) =>
{
var service = provider.GetRequiredService<IYourService>();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await service.RefreshToken());
});
});
Is it possible to add a custom DelegationHandler (outgoing request middleware) to the HttpClient built by the asp.net core TestServer?
I'm trying to combine the customization possibilities of using HttpClientFactory to influence HttpClients with the in-memory testing utility TestServer:
public class ExternalProxySslDowngradeSimulator : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken token)
{
if (request.RequestUri.Scheme == "https")
request.RequestUri = new Uri(
request.RequestUri.OriginalString.Replace("https:", "http:"));
return base.SendAsync(request, token);
}
}
[TestFixture
public class TestClass
{
[Test]
public async Task CallApi()
{
var builder = new WebHostBuilder()
.ConfigureAppConfiguration((ctx, config) => { })
.UseUrl("http://customDomain.com")
.UseStartup<CustomApi.Startup>();
var testServer = new Microsoft.AspNetCore.TestHost.TestServer(builder);
var httpClient = testServer.CreateClient();
var apiResult = await httpClient.GetAsync("https://customDomain");
}
}
I can't find a way to plug my DelegatingHandler into the HttpClient returned from testServer.CreateClient()
What I've tried so far:
Registering a custom HttpClientBuilder (did not work):
// did not work:
var builder = new WebHostBuilder()
.UseStartup<CustomApi.Startup>()
.ConfigureServices(services =>
{
services
.AddHttpClient("")
.AddHttpMessageHandler<ExternalProxySslDowngradeSimulator>();
Looking through the code for TestServer it is not hooking into that pipeline.
Custom HttpClientFactoryOptions (did not work)
Thought I could bind a custom HttpClientFactoryOptions and add a custom action to HttpClientFactoryOptions.HttpMessageHandlerBuilderActions. But the TestServer doesn't really consume this the way a default Http Client Factory does.
Customize the setup of TestServer (did not work)
Looking through the documentation / source didn't see anywhere to hook in. There's an extension method to further control DI, but I didn't see how that could get me a hook into the custom HttpMessangeHandler that TestServer is using.
So what you do is create your own client using the test server's (Server in code example) CreateHandler method and the HttpClientFactory.Create method.
var client = HttpClientFactory.Create(Server.CreateHandler(), new CustomDelegate() );
client.BaseAddress = Server.BaseAddress;
This took me forever to figure out. Hopefully this helps you.
So your example would be
var httpClient = HttpClientFactory.Create(testServer.CreateHander(), new ExternalProxySslDowngradeSimulator());
httpClient.BaseAddress = testServer.BaseAddress;
I would like to unit test a class that uses HttpClient. We injected the HttpClient object in the class constructor.
public class ClassA : IClassA
{
private readonly HttpClient _httpClient;
public ClassA(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<HttpResponseMessage> SendRequest(SomeObject someObject)
{
//Do some stuff
var request = new HttpRequestMessage(HttpMethod.Post, "http://some-domain.in");
//Build the request
var response = await _httpClient.SendAsync(request);
return response;
}
}
Now we would like to unit test the ClassA.SendRequest method. We are using Ms Test for unit testing framework and Moq for mocking.
When we tried to mock the HttpClient, it throws NotSupportedException.
[TestMethod]
public async Task SendRequestAsync_Test()
{
var mockHttpClient = new Mock<HttpClient>();
mockHttpClient.Setup(
m => m.SendAsync(It.IsAny<HttpRequestMessage>()))
.Returns(() => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
}
How can we solve this issue?
That particular overload method is not virtual so is unable to be overridden by Moq.
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
Which is why it throws NotSupportedException
The virtual method you are looking for is this method
public virtual Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
However mocking HttpClient is not as simple as it seems with its internal message handler.
I suggest using a concrete client with a custom message handler stub that will allow for more flexibility when faking the request.
Here is an example of a delegating handler stub.
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);
}
}
Note the default constructor is doing basically what you were trying to mock before. It also allows for more custom scenarios with a delegate for the request.
With the stub, the test can be refactored to something like
public async Task _SendRequestAsync_Test() {
//Arrange
var handlerStub = new DelegatingHandlerStub();
var client = new HttpClient(handlerStub);
var sut = new ClassA(client);
var obj = new SomeObject() {
//Populate
};
//Act
var response = await sut.SendRequest(obj);
//Assert
Assert.IsNotNull(response);
Assert.IsTrue(response.IsSuccessStatusCode);
}
Moq can mock out protected methods, such as SendAsync on the HttpMessageHandler that you can provide to HttpClient in its constructor.
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
});
var client = new HttpClient(mockHttpMessageHandler.Object);
Copied from https://www.thecodebuzz.com/unit-test-mock-httpclientfactory-moq-net-core/
Propper mocking with HttpClient is hard work as it was written before most people did unit testing in dotnet. Sometimes I setup a stub HTTP server that returns canned responses based on pattern matching the request url, meaning you test real HTTP requests not mocks but to a localhost server. Using WireMock.net makes this really easy and runs fast enough to satisfy most of my unit testing needs.
So instead of http://some-domain.in use a localhost server setup on some port, and then:
var server = FluentMockServer.Start(/*server and port can be setup here*/);
server.Given(
Request.Create()
.WithPath("/").UsingPost()
)
.RespondWith(
Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBody("{'attr':'value'}")
);
You can find a more details and guidance on using wiremock in tests here.
I recently had to mock HttpClient, and I used Moq.Contrib.HttpClient. It was what I needed, and simple to use, so I thought I'd throw it out there.
Here is an example of general usage:
// All requests made with HttpClient go through its handler's SendAsync() which we mock
var handler = new Mock<HttpMessageHandler>();
var client = handler.CreateClient();
// A simple example that returns 404 for any request
handler.SetupAnyRequest()
.ReturnsResponse(HttpStatusCode.NotFound);
// Match GET requests to an endpoint that returns json (defaults to 200 OK)
handler.SetupRequest(HttpMethod.Get, "https://example.com/api/stuff")
.ReturnsResponse(JsonConvert.SerializeObject(model), "application/json");
// Setting additional headers on the response using the optional configure action
handler.SetupRequest("https://example.com/api/stuff")
.ReturnsResponse(bytes, configure: response =>
{
response.Content.Headers.LastModified = new DateTime(2018, 3, 9);
})
.Verifiable(); // Naturally we can use Moq methods as well
// Verify methods are provided matching the setup helpers
handler.VerifyAnyRequest(Times.Exactly(3));
For more info, check out author's blog post here.
I have a web API message handler MyHandler that I want to run in OWIN pipeline as a middleware. So configuring the handler like this.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseHttpMessageHandler(new MyHandler());
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute(
"DefaultWebApi",
"{controller}/{id}",
new { id = RouteParameter.Optional });
app.UseWebApi(config);
}
}
Handler is very simple and does nothing.
public class MyHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{ // <--- breakpoint here
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
I put a break point inside SendAsync and it does break but the following base.SendAsync bombs silently and I see A first chance exception of type 'System.InvalidOperationException' occurred in System.Net.Http.dll.
I can quite easily add MyHandler to config.MessageHandlers and it will run perfect in the Web API pipeline but that's not what I want to do. I want to run MyHandler in the OWIN pipeline. Is this possible at all? It should be. Otherwise, there is no point in having the extension method UseHttpMessageHandler, I guess. Just that I couldn't figure out a way to do what I want to do.
Yeah, this experience needs to be improved as the exception is silently ignored.
For your above scenario, you would need to derive from HttpMessageHandler instead of DelegatingHandler as the delegating handler would try to delegate the request to handlers after it.(example: The exception mentions Message=The inner handler has not been assigned)
For example, the following would work:
appBuilder.UseHttpMessageHandler(new MyNonDelegatingHandler());
public class MyNonDelegatingHandler : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new StringContent("Hello!");
return Task.FromResult<HttpResponseMessage>(response);
}
}
And for creating a chain of handlers, you could do the following:
appBuilder.UseHttpMessageHandler(HttpClientFactory.CreatePipeline(innerHandler: new MyNonDelegatingMessageHandler(),
handlers: new DelegatingHandler[] { new DelegatingHandlerA(), new DelegatingHandlerB() }));
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.