Add a dynamic header to all outgoing Refit requests - c#

I am using Refit (5.1.67) as my HttpClient wrapper, in a .NET Core 3.1 app using IHttpClientFactory.
The API I am calling is secured using a client credentials token.
I am registering the client with this:
services.AddRefitClient<ISomeApiClient>().ConfigureHttpClient(c =>
c.BaseAddress = new Uri(Configuration["BaseUrlFromConfig"]));
The client has methods that look like this:
public interface ISomeApiClient
{
[Get("/api/somewhere")]
Task<IEnumerable<MyResponseObject>> GetItems([Header("X-User-Id")] string userId, [Header("Authorization")] string accessToken);
[Get("/api/somewhere-else")]
Task<MyResponseObject> GetItem([Header("X-User-Id")] string userId, [Header("Authorization")] string accessToken, int id);
}
What I want to avoid is having to explicitly pass accessToken and userId every time I call an endpoint (like above). Ideally, I want to have my client look like this:
public interface ISomeApiClient
{
[Get("/api/somewhere")]
Task<IEnumerable<MyResponseObject>> GetItems();
[Get("/api/somewhere")]
Task<IEnumerable<MyResponseObject>> GetItems(int id);
}
It feels like I need some sort of request middleware for outgoing requests, where I can add these two headers. If they were static I would just decorate the whole interface but because these are runtime values that will not work.
I cannot find any help on this one in the docs, and would appreciate any pointers.

Refit docs now explain how to do this
https://github.com/reactiveui/refit#reducing-header-boilerplate-with-delegatinghandlers-authorization-headers-worked-example
Add a header handler:
class AuthHeaderHandler : DelegatingHandler
{
private readonly IAuthTokenStore authTokenStore;
public AuthHeaderHandler(IAuthTokenStore authTokenStore)
{
this.authTokenStore = authTokenStore;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var token = await authTokenStore.GetToken();
//potentially refresh token here if it has expired etc.
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
}
then register in Startup.cs when registering the client:
services.AddTransient<AuthHeaderHandler>();
services.AddRefitClient<ISomeThirdPartyApi>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"))
.AddHttpMessageHandler<AuthHeaderHandler>();

You can use DI to inject your client where do you need it. We use it like this:
[ApiController]
public class ValuesController : ControllerBase
{
private readonly ISomeApiClient_client;
public ValuesController(ISomeApiClient client)
{
_client = client;
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "Your Oauth token");
}
[HttpGet("/")]
public async Task<ActionResult<Reply>> Index()
{
return await _client.GetMessageAsync();
}
}

Related

How to mock a method that depends on HttpClient? [duplicate]

I have some issues trying to wrap my code to be used in unit tests. The issues is this. I have the interface IHttpHandler:
public interface IHttpHandler
{
HttpClient client { get; }
}
And the class using it, HttpHandler:
public class HttpHandler : IHttpHandler
{
public HttpClient client
{
get
{
return new HttpClient();
}
}
}
And then the Connection class, which uses simpleIOC to inject the client implementation:
public class Connection
{
private IHttpHandler _httpClient;
public Connection(IHttpHandler httpClient)
{
_httpClient = httpClient;
}
}
And then I have a unit test project which has this class:
private IHttpHandler _httpClient;
[TestMethod]
public void TestMockConnection()
{
var client = new Connection(_httpClient);
client.doSomething();
// Here I want to somehow create a mock instance of the http client
// Instead of the real one. How Should I approach this?
}
Now obviously I will have methods in the Connection class that will retrieve data (JSON) from my backend. However, I want to write unit tests for this class, and obviously I don't want to write tests against the real back end, rather a mocked one. I have tried to google a good answer to this without great success. I can and have used Moq to mock before, but never on something like HttpClient. How should I approach this problem?
HttpClient's extensibility lies in the HttpMessageHandler passed to the constructor. Its intent is to allow platform specific implementations, but you can also mock it. There's no need to create a decorator wrapper for HttpClient.
If you'd prefer a DSL to using Moq, I have a library up on GitHub/Nuget that makes things a little easier: https://github.com/richardszalay/mockhttp
The Nuget Package RichardSzalay.MockHttp is available here.
var mockHttp = new MockHttpMessageHandler();
// Setup a respond for the user api (including a wildcard in the URL)
mockHttp.When("http://localhost/api/user/*")
.Respond("application/json", "{'name' : 'Test McGee'}"); // Respond with JSON
// Inject the handler or client into your application code
var client = new HttpClient(mockHttp);
var response = await client.GetAsync("http://localhost/api/user/1234");
// or without async: var response = client.GetAsync("http://localhost/api/user/1234").Result;
var json = await response.Content.ReadAsStringAsync();
// No network connection required
Console.Write(json); // {'name' : 'Test McGee'}
I agree with some of the other answers that the best approach is to mock the HttpMessageHandler inside HttpClient, rather than wrap HttpClient. This answer is unique in that it still injects HttpClient, allowing it to be a singleton or managed with dependency injection.
HttpClient is intended to be instantiated once and re-used throughout
the life of an application.
(Source).
Mocking HttpMessageHandler can be a little tricky because SendAsync is protected. Here's a complete example, using xunit and Moq.
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using Moq.Protected;
using Xunit;
// Use nuget to install xunit and Moq
namespace MockHttpClient {
class Program {
static void Main(string[] args) {
var analyzer = new SiteAnalyzer(Client);
var size = analyzer.GetContentSize("http://microsoft.com").Result;
Console.WriteLine($"Size: {size}");
}
private static readonly HttpClient Client = new HttpClient(); // Singleton
}
public class SiteAnalyzer {
public SiteAnalyzer(HttpClient httpClient) {
_httpClient = httpClient;
}
public async Task<int> GetContentSize(string uri)
{
var response = await _httpClient.GetAsync( uri );
var content = await response.Content.ReadAsStringAsync();
return content.Length;
}
private readonly HttpClient _httpClient;
}
public class SiteAnalyzerTests {
[Fact]
public async void GetContentSizeReturnsCorrectLength() {
// Arrange
const string testContent = "test content";
var mockMessageHandler = new Mock<HttpMessageHandler>();
mockMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage {
StatusCode = HttpStatusCode.OK,
Content = new StringContent(testContent)
});
var underTest = new SiteAnalyzer(new HttpClient(mockMessageHandler.Object));
// Act
var result = await underTest.GetContentSize("http://anyurl");
// Assert
Assert.Equal(testContent.Length, result);
}
}
}
Your interface exposes the concrete HttpClient class, therefore any classes that use this interface are tied to it, this means that it cannot be mocked.
HttpClient does not inherit from any interface so you will have to write your own. I suggest a decorator-like pattern:
public interface IHttpHandler
{
HttpResponseMessage Get(string url);
HttpResponseMessage Post(string url, HttpContent content);
Task<HttpResponseMessage> GetAsync(string url);
Task<HttpResponseMessage> PostAsync(string url, HttpContent content);
}
And your class will look like this:
public class HttpClientHandler : IHttpHandler
{
private HttpClient _client = new HttpClient();
public HttpResponseMessage Get(string url)
{
return GetAsync(url).Result;
}
public HttpResponseMessage Post(string url, HttpContent content)
{
return PostAsync(url, content).Result;
}
public async Task<HttpResponseMessage> GetAsync(string url)
{
return await _client.GetAsync(url);
}
public async Task<HttpResponseMessage> PostAsync(string url, HttpContent content)
{
return await _client.PostAsync(url, content);
}
}
The point in all of this is that HttpClientHandler creates its own HttpClient, you could then of course create multiple classes that implement IHttpHandler in different ways.
The main issue with this approach is that you are effectively writing a class that just calls methods in another class, however you could create a class that inherits from HttpClient (See Nkosi's example, it's a much better approach than mine). Life would be much easier if HttpClient had an interface that you could mock, unfortunately it does not.
This example is not the golden ticket however. IHttpHandler still relies on HttpResponseMessage, which belongs to System.Net.Http namespace, therefore if you do need other implementations other than HttpClient, you will have to perform some kind of mapping to convert their responses into HttpResponseMessage objects. This of course is only a problem if you need to use multiple implementations of IHttpHandler but it doesn't look like you do so it's not the end of the world, but it's something to think about.
Anyway, you can simply mock IHttpHandler without having to worry about the concrete HttpClient class as it has been abstracted away.
I recommend testing the non-async methods, as these still call the asynchronous methods but without the hassle of having to worry about unit testing asynchronous methods, see here
This is a common question, and I was heavily on the side wanting the ability to mock HttpClient, but I think I finally came to the realization that you shouldn't be mocking HttpClient. It seems logical to do so, but I think we've been brainwashed by things we see in open source libraries.
We often see "Clients" out there that we mock in our code so that we can test in isolation, so we automatically try to apply the same principle to HttpClient. HttpClient actually does a lot; you can think of it as a manager for HttpMessageHandler, so you don't wanna mock that, and that's why it still doesn't have an interface. The part that you're really interested in for unit testing, or designing your services, even, is the HttpMessageHandler since that is what returns the response, and you can mock that.
It's also worth pointing out that you should probably start treating HttpClient like a bigger deal. For example: Keep your instatiating of new HttpClients to a minimum. Reuse them, they're designed to be reused and use a crap ton less resources if you do. If you start treating it like a bigger deal, it'll feel much more wrong wanting to mock it and now the message handler will start to be the thing that you're injecting, not the client.
In other words, design your dependencies around the handler instead of the client. Even better, abstract "services" that use HttpClient which allow you to inject a handler, and use that as your injectable dependency instead. In fact, HttpClientFactor (which you should be using) is designed with extensions to inject the message handlers. Then in your tests, you can fake the handler to control the response for setting up your tests.
Wrapping HttpClient is an insane waste of time.
Update:
See Joshua Dooms's example. It's exactly what I'm recommending.
Here's a simple solution, which worked well for me.
Using the moq mocking library.
// ARRANGE
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
.Protected()
// Setup the PROTECTED method to mock
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
// prepare the expected response of the mocked http call
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':1,'value':'1'}]"),
})
.Verifiable();
// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
BaseAddress = new Uri("http://test.com/"),
};
var subjectUnderTest = new MyTestClass(httpClient);
// ACT
var result = await subjectUnderTest
.GetSomethingRemoteAsync('api/test/whatever');
// ASSERT
result.Should().NotBeNull(); // this is fluent assertions here...
result.Id.Should().Be(1);
// also check the 'http' call was like we expected it
var expectedUri = new Uri("http://test.com/api/test/whatever");
handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1), // we expected a single external request
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get // we expected a GET request
&& req.RequestUri == expectedUri // to this uri
),
ItExpr.IsAny<CancellationToken>()
);
Source: https://gingter.org/2018/07/26/how-to-mock-httpclient-in-your-net-c-unit-tests/
Building on the other answers, I suggest this code, which does not have any outside dependencies:
[TestClass]
public class MyTestClass
{
[TestMethod]
public async Task MyTestMethod()
{
var httpClient = new HttpClient(new MockHttpMessageHandler());
var content = await httpClient.GetStringAsync("http://some.fake.url");
Assert.AreEqual("Content as string", content);
}
}
public class MockHttpMessageHandler : HttpMessageHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var responseMessage = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("Content as string")
};
return await Task.FromResult(responseMessage);
}
}
As also mentioned in the comments you need to abstract away the HttpClient so as not to be coupled to it. I've done something similar in the past. I'll try to adapt what I did with what you are trying to do.
First look at the HttpClient class and decided on what functionality it provided that would be needed.
Here is a possibility:
public interface IHttpClient {
System.Threading.Tasks.Task<T> DeleteAsync<T>(string uri) where T : class;
System.Threading.Tasks.Task<T> DeleteAsync<T>(Uri uri) where T : class;
System.Threading.Tasks.Task<T> GetAsync<T>(string uri) where T : class;
System.Threading.Tasks.Task<T> GetAsync<T>(Uri uri) where T : class;
System.Threading.Tasks.Task<T> PostAsync<T>(string uri, object package);
System.Threading.Tasks.Task<T> PostAsync<T>(Uri uri, object package);
System.Threading.Tasks.Task<T> PutAsync<T>(string uri, object package);
System.Threading.Tasks.Task<T> PutAsync<T>(Uri uri, object package);
}
Again as stated before this was for particular purposes. I completely abstracted away most dependencies to anything dealing with HttpClient and focused on what I wanted returned. You should evaluate how you want to abstract the HttpClient to provide only the necessary functionality you want.
This will now allow you to mock only what is needed to be tested.
I would even recommend doing away with IHttpHandler completely and use the HttpClient abstraction IHttpClient. But I'm just not picking as you can replace the body of your handler interface with the members of the abstracted client.
An implementation of the IHttpClient can then be used to wrapp/adapt a real/concrete HttpClient or any other object for that matter, that can be used to make HTTP requests as what you really wanted was a service that provided that functionality as apposed to HttpClient specifically. Using the abstraction is a clean (My opinion) and SOLID approach and can make your code more maintainable if you need to switch out the underlying client for something else as the framework changes.
Here is a snippet of how an implementation could be done.
/// <summary>
/// HTTP Client adaptor wraps a <see cref="System.Net.Http.HttpClient"/>
/// that contains a reference to <see cref="ConfigurableMessageHandler"/>
/// </summary>
public sealed class HttpClientAdaptor : IHttpClient {
HttpClient httpClient;
public HttpClientAdaptor(IHttpClientFactory httpClientFactory) {
httpClient = httpClientFactory.CreateHttpClient(**Custom configurations**);
}
//...other code
/// <summary>
/// Send a GET request to the specified Uri as an asynchronous operation.
/// </summary>
/// <typeparam name="T">Response type</typeparam>
/// <param name="uri">The Uri the request is sent to</param>
/// <returns></returns>
public async System.Threading.Tasks.Task<T> GetAsync<T>(Uri uri) where T : class {
var result = default(T);
//Try to get content as T
try {
//send request and get the response
var response = await httpClient.GetAsync(uri).ConfigureAwait(false);
//if there is content in response to deserialize
if (response.Content.Headers.ContentLength.GetValueOrDefault() > 0) {
//get the content
string responseBodyAsText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
//desrialize it
result = deserializeJsonToObject<T>(responseBodyAsText);
}
} catch (Exception ex) {
Log.Error(ex);
}
return result;
}
//...other code
}
As you can see in the above example, a lot of the heavy lifting usually associated with using HttpClient is hidden behind the abstraction.
You connection class can then be inject with the abstracted client
public class Connection
{
private IHttpClient _httpClient;
public Connection(IHttpClient httpClient)
{
_httpClient = httpClient;
}
}
Your test can then mock what is needed for your SUT
private IHttpClient _httpClient;
[TestMethod]
public void TestMockConnection()
{
SomeModelObject model = new SomeModelObject();
var httpClientMock = new Mock<IHttpClient>();
httpClientMock.Setup(c => c.GetAsync<SomeModelObject>(It.IsAny<string>()))
.Returns(() => Task.FromResult(model));
_httpClient = httpClientMock.Object;
var client = new Connection(_httpClient);
// Assuming doSomething uses the client to make
// a request for a model of type SomeModelObject
client.doSomething();
}
I think the issue is that you've got it just a little upside down.
public class AuroraClient : IAuroraClient
{
private readonly HttpClient _client;
public AuroraClient() : this(new HttpClientHandler())
{
}
public AuroraClient(HttpMessageHandler messageHandler)
{
_client = new HttpClient(messageHandler);
}
}
If you look at the class above, I think this is what you want. Microsoft recommends keeping the client alive for optimal performance, so this type of structure allows you to do that. Also the HttpMessageHandler is an abstract class and therefore mockable. Your test method would then look like this:
[TestMethod]
public void TestMethod1()
{
// Arrange
var mockMessageHandler = new Mock<HttpMessageHandler>();
// Set up your mock behavior here
var auroraClient = new AuroraClient(mockMessageHandler.Object);
// Act
// Assert
}
This allows you to test your logic while mocking the HttpClient's behavior.
Sorry guys, after writing this and trying it myself, I realized that you can't mock the protected methods on the HttpMessageHandler. I subsequently added the following code to allow for injection of a proper mock.
public interface IMockHttpMessageHandler
{
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
}
public class MockHttpMessageHandler : HttpMessageHandler
{
private readonly IMockHttpMessageHandler _realMockHandler;
public MockHttpMessageHandler(IMockHttpMessageHandler realMockHandler)
{
_realMockHandler = realMockHandler;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return await _realMockHandler.SendAsync(request, cancellationToken);
}
}
The tests written with this then look something like the following:
[TestMethod]
public async Task GetProductsReturnsDeserializedXmlXopData()
{
// Arrange
var mockMessageHandler = new Mock<IMockHttpMessageHandler>();
// Set up Mock behavior here.
var client = new AuroraClient(new MockHttpMessageHandler(mockMessageHandler.Object));
// Act
// Assert
}
One alternative would be to setup a stub HTTP server that returns canned responses based on pattern matching the request url, meaning you test real HTTP requests not mocks. Historically this would have taken significant develoment effort and would have been far to slow to be considered for unit testing, however OSS library WireMock.net is easy to use and fast enough to be run with lots of tests so may be worth considering. Setup is a few lines of code:
var server = FluentMockServer.Start();
server.Given(
Request.Create()
.WithPath("/some/thing").UsingGet()
)
.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.
One of my colleagues noticed that most of the HttpClient methods all call SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) under the hood, which is a virtual method off of HttpMessageInvoker:
So by far the easiest way to mock out HttpClient was to simply mock that particular method:
var mockClient = new Mock<HttpClient>();
mockClient.Setup(client => client.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>())).ReturnsAsync(_mockResponse.Object);
and your code can call most (but not all) of the HttpClient class methods, including a regular
httpClient.SendAsync(req)
Check here to confirm
https://github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/System/Net/Http/HttpClient.cs
Microsoft now offers the alternative to use a IHttpClientFactory instead of directly using HttpClient:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0
Example Mock with request that returns expected result:
private LoginController GetLoginController()
{
var expected = "Hello world";
var mockFactory = new Mock<IHttpClientFactory>();
var mockMessageHandler = new Mock<HttpMessageHandler>();
mockMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(expected)
});
var httpClient = new HttpClient(mockMessageHandler.Object);
mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient);
var logger = Mock.Of<ILogger<LoginController>>();
var controller = new LoginController(logger, mockFactory.Object);
return controller;
}
Source:
https://stackoverflow.com/a/66256132/3850405
I'm not convinced by many of the answers.
First of all, imagine you want to unit test a method that uses HttpClient. You should not instantiate HttpClient directly in your implementation. You should inject a factory with the responsibility of providing an instance of HttpClient for you. That way you can mock later on that factory and return whichever HttpClient you want (e.g: a mock HttpClient and not the real one).
So, you would have a factory like the following:
public interface IHttpClientFactory
{
HttpClient Create();
}
And an implementation:
public class HttpClientFactory
: IHttpClientFactory
{
public HttpClient Create()
{
var httpClient = new HttpClient();
return httpClient;
}
}
Of course you would need to register in your IoC Container this implementation. If you use Autofac it would be something like:
builder
.RegisterType<IHttpClientFactory>()
.As<HttpClientFactory>()
.SingleInstance();
Now you would have a proper and testeable implementation. Imagine that your method is something like:
public class MyHttpClient
: IMyHttpClient
{
private readonly IHttpClientFactory _httpClientFactory;
public SalesOrderHttpClient(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<string> PostAsync(Uri uri, string content)
{
using (var client = _httpClientFactory.Create())
{
var clientAddress = uri.GetLeftPart(UriPartial.Authority);
client.BaseAddress = new Uri(clientAddress);
var content = new StringContent(content, Encoding.UTF8, "application/json");
var uriAbsolutePath = uri.AbsolutePath;
var response = await client.PostAsync(uriAbsolutePath, content);
var responseJson = response.Content.ReadAsStringAsync().Result;
return responseJson;
}
}
}
Now the testing part. HttpClient extends HttpMessageHandler, which is abstract. Let's create a "mock" of HttpMessageHandler that accepts a delegate so that when we use the mock we can also setup each behaviour for each test.
public class MockHttpMessageHandler
: HttpMessageHandler
{
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _sendAsyncFunc;
public MockHttpMessageHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> sendAsyncFunc)
{
_sendAsyncFunc = sendAsyncFunc;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return await _sendAsyncFunc.Invoke(request, cancellationToken);
}
}
And now, and with the help of Moq (and FluentAssertions, a library that makes unit tests more readable), we have everything needed to unit test our method PostAsync that uses HttpClient
public static class PostAsyncTests
{
public class Given_A_Uri_And_A_JsonMessage_When_Posting_Async
: Given_WhenAsync_Then_Test
{
private SalesOrderHttpClient _sut;
private Uri _uri;
private string _content;
private string _expectedResult;
private string _result;
protected override void Given()
{
_uri = new Uri("http://test.com/api/resources");
_content = "{\"foo\": \"bar\"}";
_expectedResult = "{\"result\": \"ok\"}";
var httpClientFactoryMock = new Mock<IHttpClientFactory>();
var messageHandlerMock =
new MockHttpMessageHandler((request, cancellation) =>
{
var responseMessage =
new HttpResponseMessage(HttpStatusCode.Created)
{
Content = new StringContent("{\"result\": \"ok\"}")
};
var result = Task.FromResult(responseMessage);
return result;
});
var httpClient = new HttpClient(messageHandlerMock);
httpClientFactoryMock
.Setup(x => x.Create())
.Returns(httpClient);
var httpClientFactory = httpClientFactoryMock.Object;
_sut = new SalesOrderHttpClient(httpClientFactory);
}
protected override async Task WhenAsync()
{
_result = await _sut.PostAsync(_uri, _content);
}
[Fact]
public void Then_It_Should_Return_A_Valid_JsonMessage()
{
_result.Should().BeEquivalentTo(_expectedResult);
}
}
}
Obviously this test is silly, and we're really testing our mock. But you get the idea. You should test meaningful logic depending on your implementation such as..
if the code status of the response is not 201, should it throw an exception?
if the response text cannot be parsed, what should happen?
etc.
The purpose of this answer was to test something that uses HttpClient and this is a nice clean way to do so.
UPDATE
Lately I use an http builder in my tests where I can easily inject the json response I expect.
public class HttpClientBuilder
{
private HttpMessageHandler _httpMessageHandler = new HttpClientHandler();
public HttpClientBuilder WithJsonResponse(HttpStatusCode httpStatusCode, string json, string contentType = "application/json")
{
var mockHttpMessageHandler =
new MockHttpMessageHandler(
(request, cancellation) =>
{
var responseMessage =
new HttpResponseMessage(httpStatusCode)
{
Content = new StringContent(json, Encoding.UTF8, contentType)
};
var result = Task.FromResult(responseMessage);
return result;
});
_httpMessageHandler = mockHttpMessageHandler;
return this;
}
public HttpClient Build()
{
var httpClient = new HttpClient(_httpMessageHandler);
return httpClient;
}
}
class MockHttpMessageHandler
: HttpMessageHandler
{
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _sendAsyncFunc;
public MockHttpMessageHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> sendAsyncFunc)
{
_sendAsyncFunc = sendAsyncFunc;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return await _sendAsyncFunc.Invoke(request, cancellationToken);
}
}
so, as long as I have the HttpClient behind an abstraction like IHttpClientFactory, as I've suggested above, in my tests I can do something like
var httpClientFactoryMock = new Mock<IHttpClientFactory>();
var jsonResponse = "{\"hello world\"}";
var httpClient =
new HttpClientBuilder()
.WithJsonResponse(HttpStatusCode.OK, jsonResponse)
.Build();
httpClientFactoryMock
.Setup(x => x.Create())
.Returns(httpClient);
var httpClientFactory = httpClientFactoryMock.Object;
and then use that httpClientFactory.
DO NOT have a wrapper that creates a new instance of HttpClient. If you do that, you will run out of sockets at runtime (even though you are disposing the HttpClient object).
If using MOQ, the correct way to do this is to add using Moq.Protected; to your test and then write code like the following:
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("It worked!")
};
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(() => response);
var httpClient = new HttpClient(mockHttpMessageHandler.Object);
Joining the party a bit late, but I like using wiremocking (https://github.com/WireMock-Net/WireMock.Net) whenever possible in the integrationtest of a dotnet core microservice with downstream REST dependencies.
By implementing a TestHttpClientFactory extending the IHttpClientFactory we can override the method
HttpClient CreateClient(string name)
So when using the named clients within your app you are in control of returning a HttpClient wired to your wiremock.
The good thing about this approach is that you are not changing anything within the application you are testing, and enables course integration tests doing an actual REST request to your service and mocking the json (or whatever) the actual downstream request should return. This leads to concise tests and as little mocking as possible in your application.
public class TestHttpClientFactory : IHttpClientFactory
{
public HttpClient CreateClient(string name)
{
var httpClient = new HttpClient
{
BaseAddress = new Uri(G.Config.Get<string>($"App:Endpoints:{name}"))
// G.Config is our singleton config access, so the endpoint
// to the running wiremock is used in the test
};
return httpClient;
}
}
and
// in bootstrap of your Microservice
IHttpClientFactory factory = new TestHttpClientFactory();
container.Register<IHttpClientFactory>(factory);
There are several different approaches to mock an HttpClient. Here are a few that I've done some POC's on using xUnit before deciding on a single solution (Moq.Contrib.HttpClient). Note that there are many more abilities to each framework than what I show below; I kept each example succinct for clarity.
Moq (by itself)
This is relatively straightforward if you are familiar with using the Moq framework. The "trick" is to mock the HttpMessageHandler inside of the HttpClient - not the HttpClient itself. NOTE: It is good practice to use MockBehavior.Strict in the mock so that you are alerted to any calls that you have not explicitly mocked-out and were expecting.
RichardSzalay.MockHttp
RichardSzalay.MockHttp is another popular solution. I've used this in the past but found it slightly more cumbersome that Moq.Contrib.HttpClient. There are two different patterns that can be used here. Richard describes when to use one vs the other here.
Moq.Contrib.HttpClient
Like the solution for using Moq by itself, this is straightforward if you are familiar with using the Moq framework. I found this solution to be slightly more direct with less code. This is the solution I opted to use. Note that this solution requires a separate Nuget from Moq itself - Moq.Contrib.HttpClient
WireMock.Net
A newcomer to the game, WireMock.net is gaining popularity. This would be a reasonable solution instead of Microsoft.AspNetCore.TestHost if you are writing Integration Tests where calls to the endpoint are actually made instead of being mocked. I thought this would be my pick at first but decided against it for two reasons:
it does actually open ports to facilitate the test. Since I've had to fix port-exhaustion issues from improper usage of HttpClient in the past, I decided to pass on this solution as I wasn't sure how well it would scale across a large codebase with many unit tests running in parallel.
The urls used must be resolvable (actual legit urls). If you want the simplicity of not caring about a "real" url (just that the url you expected was actually called) then this may not be for you.
Example
Given the following simplistic/contrived code, here is how you would write each test.
public class ClassUnderTest
{
private readonly HttpClient _httpClient;
private const string Url = "https://myurl";
public ClassUnderTest(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<Person> GetPersonAsync(int id)
{
var response = await _httpClient.GetAsync($"{Url}?id={id}");
return await response.Content.ReadFromJsonAsync<Person>();
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
Moq (by itself)
[Fact]
public async Task JustMoq()
{
//arrange
const int personId = 1;
var mockHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
var dto = new Person { Id = personId, Name = "Dave", Age = 42 };
var mockResponse = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = JsonContent.Create<Person>(dto)
};
mockHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.Is<HttpRequestMessage>(m => m.Method == HttpMethod.Get),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(mockResponse);
// Inject the handler or client into your application code
var httpClient = new HttpClient(mockHandler.Object);
var sut = new ClassUnderTest(httpClient);
//act
var actual = await sut.GetPersonAsync(personId);
//assert
Assert.NotNull(actual);
mockHandler.Protected().Verify(
"SendAsync",
Times.Exactly(1),
ItExpr.Is<HttpRequestMessage>(m => m.Method == HttpMethod.Get),
ItExpr.IsAny<CancellationToken>());
}
RichardSzalay.MockHttp (using BackendDefinition pattern)
[Fact]
public async Task RichardSzalayMockHttpUsingBackendDefinition()
{
//arrange
const int personId = 1;
using var mockHandler = new MockHttpMessageHandler();
var dto = new Person { Id = personId, Name = "Dave", Age = 42 };
var mockResponse = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = JsonContent.Create<Person>(dto)
};
var mockedRequest = mockHandler.When(HttpMethod.Get, "https://myurl?id=1")
.Respond(mockResponse.StatusCode, mockResponse.Content);
// Inject the handler or client into your application code
var httpClient = mockHandler.ToHttpClient();
var sut = new ClassUnderTest(httpClient);
//act
var actual = await sut.GetPersonAsync(personId);
//assert
Assert.NotNull(actual);
Assert.Equivalent(dto, actual);
Assert.Equal(1, mockHandler.GetMatchCount(mockedRequest));
mockHandler.VerifyNoOutstandingRequest();
}
RichardSzalay.MockHttp (using RequestExpectation pattern)
[Fact]
public async Task RichardSzalayMockHttpUsingRequestExpectation()
{
//arrange
const int personId = 1;
using var mockHandler = new MockHttpMessageHandler();
var dto = new Person { Id = personId, Name = "Dave", Age = 42 };
var mockResponse = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = JsonContent.Create<Person>(dto)
};
var mockedRequest = mockHandler.Expect(HttpMethod.Get, "https://myurl")
.WithExactQueryString($"id={personId}")
.Respond(mockResponse.StatusCode, mockResponse.Content);
// Inject the handler or client into your application code
var httpClient = mockHandler.ToHttpClient();
var sut = new ClassUnderTest(httpClient);
//act
var actual = await sut.GetPersonAsync(personId);
//assert
Assert.NotNull(actual);
Assert.Equivalent(dto, actual);
Assert.Equal(1, mockHandler.GetMatchCount(mockedRequest));
mockHandler.VerifyNoOutstandingExpectation();
}
Moq.Contrib.HttpClient
[Fact]
public async Task UsingMoqContribHttpClient()
{
//arrange
const int personId = 1;
var mockHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
var dto = new Person { Id = personId, Name = "Dave", Age = 42 };
var mockUrl = $"https://myurl?id={personId}";
var mockResponse = mockHandler.SetupRequest(HttpMethod.Get, mockUrl)
.ReturnsJsonResponse<Person>(HttpStatusCode.OK, dto);
// Inject the handler or client into your application code
var httpClient = mockHandler.CreateClient();
var sut = new ClassUnderTest(httpClient);
//act
var actual = await sut.GetPersonAsync(personId);
//assert
Assert.NotNull(actual);
Assert.Equivalent(dto, actual);
mockHandler.VerifyRequest(HttpMethod.Get, mockUrl, Times.Once());
}
WireMock.NET
public class TestClass : IDisposable
{
private WireMockServer _server;
public TestClass()
{
_server = WireMockServer.Start();
}
public void Dispose()
{
_server.Stop();
}
[Fact]
public async Task UsingWireMock()
{
//arrange
const int personId = 1;
var dto = new Person { Id = personId, Name = "Dave", Age = 42 };
var mockUrl = $"https://myurl?id={personId}";
_server.Given(
Request.Create()
.WithPath("/"))
.RespondWith(
Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson(dto));
// Inject the handler or client into your application code
var httpClient = _server.CreateClient();
var sut = new ClassUnderTest(httpClient);
//act
var actual = await sut.GetPersonAsync(personId);
//assert
Assert.NotNull(actual);
Assert.Equivalent(dto, actual);
}
}
If you don't mind running your own http server, you can try Xim. It's as easy as this:
using Xim.Simulators.Api;
[Test]
public async Task TestHttpGetMethod()
{
using var simulation = Simulation.Create();
using var api = simulation
.AddApi()
.AddHandler("GET /books/1234", ApiResponse.Ok())
.Build();
await api.StartAsync();
var httpClient = new HttpClient();
var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, $"{api.Location}/books/1234"));
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
Assert.IsTrue(api.ReceivedApiCalls.Any(call => call.Action == "GET /books/1234"));
}
It's a nice alternative to using mocks and may suit your needs in some scenarios. It's built on top of Kestrel (and yes, I'm the author).
All you need is a test version of HttpMessageHandler class which you pass to HttpClient ctor. The main point is that your test HttpMessageHandler class will have a HttpRequestHandler delegate that the callers can set and simply handle the HttpRequest the way they want.
public class FakeHttpMessageHandler : HttpMessageHandler
{
public Func<HttpRequestMessage, CancellationToken, HttpResponseMessage> HttpRequestHandler { get; set; } =
(r, c) =>
new HttpResponseMessage
{
ReasonPhrase = r.RequestUri.AbsoluteUri,
StatusCode = HttpStatusCode.OK
};
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(HttpRequestHandler(request, cancellationToken));
}
}
You can use an instance of this class to create a concrete HttpClient instance. Via the HttpRequestHandler delegate you have full control over outgoing http requests from HttpClient.
After carefully searching, I figured out the best approach to accomplish this.
private HttpResponseMessage response;
[SetUp]
public void Setup()
{
var handlerMock = new Mock<HttpMessageHandler>();
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
// This line will let you to change the response in each test method
.ReturnsAsync(() => response);
_httpClient = new HttpClient(handlerMock.Object);
yourClinet = new YourClient( _httpClient);
}
As you noticed I have used Moq and Moq.Protected packages.
This is an old question, but I feel the urge to extend the answers with a solution I didn't see here.
You can fake the Microsoft assemly (System.Net.Http) and then use ShinsContext during the test.
In VS 2017, right click on the System.Net.Http assembly and choose "Add Fakes Assembly"
Put your code in the unit test method under a ShimsContext.Create() using. This way, you can isolate the code where you are planning to fake the HttpClient.
Depends on your implementation and test, I would suggest to implement all the desired acting where you call a method on the HttpClient and want to fake the returned value. Using ShimHttpClient.AllInstances will fake your implementation in all the instances created during your test. For example, if you want to fake the GetAsync() method, do the following:
[TestMethod]
public void FakeHttpClient()
{
using (ShimsContext.Create())
{
System.Net.Http.Fakes.ShimHttpClient.AllInstances.GetAsyncString = (c, requestUri) =>
{
//Return a service unavailable response
var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.ServiceUnavailable);
var task = Task.FromResult(httpResponseMessage);
return task;
};
//your implementation will use the fake method(s) automatically
var client = new Connection(_httpClient);
client.doSomething();
}
}
Since HttpClient use SendAsync method to perform all HTTP Requests, you can override SendAsync method and mock the HttpClient.
For that wrap creating HttpClient to a interface, something like below
public interface IServiceHelper
{
HttpClient GetClient();
}
Then use above interface for dependency injection in your service, sample below
public class SampleService
{
private readonly IServiceHelper serviceHelper;
public SampleService(IServiceHelper serviceHelper)
{
this.serviceHelper = serviceHelper;
}
public async Task<HttpResponseMessage> Get(int dummyParam)
{
try
{
var dummyUrl = "http://www.dummyurl.com/api/controller/" + dummyParam;
var client = serviceHelper.GetClient();
HttpResponseMessage response = await client.GetAsync(dummyUrl);
return response;
}
catch (Exception)
{
// log.
throw;
}
}
}
Now in unit test project create a helper class for mocking SendAsync.
Here it is a FakeHttpResponseHandler class which is inheriting DelegatingHandler which will provide an option to override the SendAsync method. After overriding the SendAsync method need to setup a response for each HTTP Request which is calling SendAsync method, for that create a Dictionary with key as Uri and value as HttpResponseMessage so that whenever there is a HTTP Request and if the Uri matches SendAsync will return the configured HttpResponseMessage.
public class FakeHttpResponseHandler : DelegatingHandler
{
private readonly IDictionary<Uri, HttpResponseMessage> fakeServiceResponse;
private readonly JavaScriptSerializer javaScriptSerializer;
public FakeHttpResponseHandler()
{
fakeServiceResponse = new Dictionary<Uri, HttpResponseMessage>();
javaScriptSerializer = new JavaScriptSerializer();
}
/// <summary>
/// Used for adding fake httpResponseMessage for the httpClient operation.
/// </summary>
/// <typeparam name="TQueryStringParameter"> query string parameter </typeparam>
/// <param name="uri">Service end point URL.</param>
/// <param name="httpResponseMessage"> Response expected when the service called.</param>
public void AddFakeServiceResponse(Uri uri, HttpResponseMessage httpResponseMessage)
{
fakeServiceResponse.Remove(uri);
fakeServiceResponse.Add(uri, httpResponseMessage);
}
/// <summary>
/// Used for adding fake httpResponseMessage for the httpClient operation having query string parameter.
/// </summary>
/// <typeparam name="TQueryStringParameter"> query string parameter </typeparam>
/// <param name="uri">Service end point URL.</param>
/// <param name="httpResponseMessage"> Response expected when the service called.</param>
/// <param name="requestParameter">Query string parameter.</param>
public void AddFakeServiceResponse<TQueryStringParameter>(Uri uri, HttpResponseMessage httpResponseMessage, TQueryStringParameter requestParameter)
{
var serilizedQueryStringParameter = javaScriptSerializer.Serialize(requestParameter);
var actualUri = new Uri(string.Concat(uri, serilizedQueryStringParameter));
fakeServiceResponse.Remove(actualUri);
fakeServiceResponse.Add(actualUri, httpResponseMessage);
}
// all method in HttpClient call use SendAsync method internally so we are overriding that method here.
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if(fakeServiceResponse.ContainsKey(request.RequestUri))
{
return Task.FromResult(fakeServiceResponse[request.RequestUri]);
}
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound)
{
RequestMessage = request,
Content = new StringContent("Not matching fake found")
});
}
}
Create a new implementation for IServiceHelper by mocking framework or like below.
This FakeServiceHelper class we can use to inject the FakeHttpResponseHandler class so that whenever the HttpClient created by this class it will use FakeHttpResponseHandler class instead of the actual implementation.
public class FakeServiceHelper : IServiceHelper
{
private readonly DelegatingHandler delegatingHandler;
public FakeServiceHelper(DelegatingHandler delegatingHandler)
{
this.delegatingHandler = delegatingHandler;
}
public HttpClient GetClient()
{
return new HttpClient(delegatingHandler);
}
}
And in test configure FakeHttpResponseHandler class by adding the Uri and expected HttpResponseMessage.
The Uri should be the actual serviceendpoint Uri so that when the overridden SendAsync method is called from actual service implementation it will match the Uri in Dictionary and respond with the configured HttpResponseMessage.
After configuring inject the FakeHttpResponseHandler object to the fake IServiceHelper implementation.
Then inject the FakeServiceHelper class to the actual service which will make the actual service to use the override SendAsync method.
[TestClass]
public class SampleServiceTest
{
private FakeHttpResponseHandler fakeHttpResponseHandler;
[TestInitialize]
public void Initialize()
{
fakeHttpResponseHandler = new FakeHttpResponseHandler();
}
[TestMethod]
public async Task GetMethodShouldReturnFakeResponse()
{
Uri uri = new Uri("http://www.dummyurl.com/api/controller/");
const int dummyParam = 123456;
const string expectdBody = "Expected Response";
var expectedHttpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(expectdBody)
};
fakeHttpResponseHandler.AddFakeServiceResponse(uri, expectedHttpResponseMessage, dummyParam);
var fakeServiceHelper = new FakeServiceHelper(fakeHttpResponseHandler);
var sut = new SampleService(fakeServiceHelper);
var response = await sut.Get(dummyParam);
var responseBody = await response.Content.ReadAsStringAsync();
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
Assert.AreEqual(expectdBody, responseBody);
}
}
GitHub Link : which is having sample implementation
I did something very simple, as I was in a DI environment.
public class HttpHelper : IHttpHelper
{
private ILogHelper _logHelper;
public HttpHelper(ILogHelper logHelper)
{
_logHelper = logHelper;
}
public virtual async Task<HttpResponseMessage> GetAsync(string uri, Dictionary<string, string> headers = null)
{
HttpResponseMessage response;
using (var client = new HttpClient())
{
if (headers != null)
{
foreach (var h in headers)
{
client.DefaultRequestHeaders.Add(h.Key, h.Value);
}
}
response = await client.GetAsync(uri);
}
return response;
}
public async Task<T> GetAsync<T>(string uri, Dictionary<string, string> headers = null)
{
...
rawResponse = await GetAsync(uri, headers);
...
}
}
and the mock is:
[TestInitialize]
public void Initialize()
{
...
_httpHelper = new Mock<HttpHelper>(_logHelper.Object) { CallBase = true };
...
}
[TestMethod]
public async Task SuccessStatusCode_WithAuthHeader()
{
...
_httpHelper.Setup(m => m.GetAsync(_uri, myHeaders)).Returns(
Task<HttpResponseMessage>.Factory.StartNew(() =>
{
return new HttpResponseMessage(System.Net.HttpStatusCode.OK)
{
Content = new StringContent(JsonConvert.SerializeObject(_testData))
};
})
);
var result = await _httpHelper.Object.GetAsync<TestDTO>(...);
Assert.AreEqual(...);
}
Inspired by PointZeroTwo's answer, here's a sample using NUnit and FakeItEasy.
SystemUnderTest in this example is the class that you want to test - no sample content given for it but I assume you have that already!
[TestFixture]
public class HttpClientTests
{
private ISystemUnderTest _systemUnderTest;
private HttpMessageHandler _mockMessageHandler;
[SetUp]
public void Setup()
{
_mockMessageHandler = A.Fake<HttpMessageHandler>();
var httpClient = new HttpClient(_mockMessageHandler);
_systemUnderTest = new SystemUnderTest(httpClient);
}
[Test]
public void HttpError()
{
// Arrange
A.CallTo(_mockMessageHandler)
.Where(x => x.Method.Name == "SendAsync")
.WithReturnType<Task<HttpResponseMessage>>()
.Returns(Task.FromResult(new HttpResponseMessage
{
StatusCode = HttpStatusCode.InternalServerError,
Content = new StringContent("abcd")
}));
// Act
var result = _systemUnderTest.DoSomething();
// Assert
// Assert.AreEqual(...);
}
}
Perhaps there would be some code to change in your current project but for new projects you should absolutely consider using Flurl.
https://flurl.dev
It is a HTTP client library for .NET with a fluent interface that specifically enables testability for code that uses it to make HTTP requests.
There are plenty of code samples on the website but in a nutshell you use it like this in your code.
Add the usings.
using Flurl;
using Flurl.Http;
Send a get request and read the response.
public async Task SendGetRequest()
{
var response = await "https://example.com".GetAsync();
// ...
}
In the unit tests Flurl acts as a mock that can be configured to behave as desired and also to verify the calls that were done.
using (var httpTest = new HttpTest())
{
// Arrange
httpTest.RespondWith("OK", 200);
// Act
await sut.SendGetRequest();
// Assert
httpTest.ShouldHaveCalled("https://example.com")
.WithVerb(HttpMethod.Get);
}
To add my 2 cents. To mock specific http request methods either Get or Post. This worked for me.
mockHttpMessageHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.Is<HttpRequestMessage>(a => a.Method == HttpMethod.Get), ItExpr.IsAny<CancellationToken>())
.Returns(Task.FromResult(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(""),
})).Verifiable();

Using DelegatingHandler with custom data on HttpClient

Given the well known dilemma's and issues of using HttpClient - namely socket exhaustion and not respecting DNS updates, its considered best practice to use IHttpClientFactory and let the container decide when and how to utilise http pool connections efficiency. Which is all good, but now I cannot instantiate a custom DelegatingHandler with custom data on each request.
Sample below on how I did it before using the factory method:
public class HttpClientInterceptor : DelegatingHandler
{
private readonly int _id;
public HttpClientInterceptor(int id)
{
_id = id;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// associate the id with this request
Database.InsertEnquiry(_id, request);
return await base.SendAsync(request, cancellationToken);
}
}
For every time I instantiate a HttpClient a Id can be passed along:
public void DoEnquiry()
{
// Insert a new enquiry hypothetically
int id = Database.InsertNewEnquiry();
using (var http = new HttpClient(new HttpClientInterceptor(id)))
{
// and do some operations on the http client
// which will be logged in the database associated with id
http.GetStringAsync("http://url.com");
}
}
But now I cannot instantiate the HttpClient nor the Handlers.
public void DoEnquiry(IHttpClientFactory factory)
{
int id = Database.InsertNewEnquiry();
var http = factory.CreateClient();
// and now??
http.GetStringAsync("http://url.com");
}
How would I be able to achieve similar using the factory?
I cannot instantiate a custom DelegatingHandler with custom data on each request.
This is correct. But you can use a custom DelegatingHandler that is reusable (and stateless), and pass the data as part of the request. This is what HttpRequestMessage.Properties is for. When doing these kinds of "custom context" operations, I prefer to define my context "property" as an extension method:
public static class HttpRequestExtensions
{
public static HttpRequestMessage WithId(this HttpRequestMessage request, int id)
{
request.Properties[IdKey] = id;
return request;
}
public static int? TryGetId(this HttpRequestMessage request)
{
if (request.Properties.TryGetValue(IdKey, out var value))
return value as int?;
return null;
}
private static readonly string IdKey = Guid.NewGuid().ToString("N");
}
Then you can use it as such in a (reusable) DelegatingHandler:
public class HttpClientInterceptor : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var id = request.TryGetId();
if (id == null)
throw new InvalidOperationException("This request must have an id set.");
// associate the id with this request
Database.InsertEnquiry(id.Value, request);
return await base.SendAsync(request, cancellationToken);
}
}
The disadvantage to this approach is that each callsite then has to specify the id. This means you can't use the built-in convenience methods like GetStringAsync; if you do, the exception above will be thrown. Instead, your code will have to use the lower-level SendAsync method:
var request = new HttpRequestMessage(HttpMethod.Get, "http://url.com");
request.SetId(id);
var response = await client.SendAsync(request);
The calling boilerplate is rather ugly. You can wrap this up into your own GetStringAsync convenience method; something like this should work:
public static class HttpClientExtensions
{
public static async Task<string> GetStringAsync(int id, string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.SetId(id);
var response = await client.SendAsync(request);
return await response.Content.ReadAsStringAsync();
}
}
and now your call sites end up looking cleaner again:
public async Task DoEnquiry(IHttpClientFactory factory)
{
int id = Database.InsertNewEnquiry();
var http = factory.CreateClient();
var result = await http.GetStringAsync(id, "http://url.com");
}

How to wrap graphql.net endpoint response using asp.net core 2 middleware?

I have REST API developed using asp.net web api2. I am migrating the REST API to GraphQL.net endpoints using asp.net core 2. In the existing REST API code I have a Delegating handler used to extend the result of REST API call with additional data which in this case is add localization data to the response.Since Delegating handler are no more supported in asp.net core 2. I am trying to migrate the existing Delegating handler to Middleware component.
For reference purpose I followed the details mentioned at : Extending WebApi response using OWIN Middleware and
https://www.devtrends.co.uk/blog/wrapping-asp.net-web-api-responses-for-consistency-and-to-provide-additional-information
Here I have couple of queries:
How to map the below code in case of Middlware ?
var response = await base.SendAsync(request, cancellationToken);
Where should I place the middleware in Startup.cs Configure method.
Middleware equivalent of the existing Delegating Handler
Code:
public class CommonResponserHandler : DelegatingHandler
{
ICommonService _commonService = new CommonService();
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
string locale = string.Empty;
if (request.Headers.Contains("Accept-Language"))
{
locale = request.Headers.GetValues("Accept-Language").First();
}
bool initialAuthorizationStatus = GetInitialAuthorization(request);
var response = await base.SendAsync(request, cancellationToken);
APIResult commonResponse;
if (response.TryGetContentValue<APIResult>(out commonResponse))
{
//populate common response here;
UpdateCommonResponse(request, response, commonResponse);
//UpdateCommonResponse(basicResponse, commonResponse);
HttpResponseMessage newResponse;
bool authorizatinCheckResult = AssertAuthorization(initialAuthorizationStatus, request);
if (authorizatinCheckResult)
{
newResponse = request.CreateResponse(response.StatusCode, commonResponse);
}
else
{
var unAuthorisedResult = new APIResult{Authorized = false, UserMessage = Constants.Unauthorized, Locale = new Locale(_commonService.GetLanguageFromLocale(locale))};
newResponse = request.CreateResponse(HttpStatusCode.Unauthorized, unAuthorisedResult);
var jsonSerializerSettings = new JsonSerializerSettings{ContractResolver = new CamelCasePropertyNamesContractResolver()};
HttpContext.Current.Items["401message"] = JsonConvert.SerializeObject(unAuthorisedResult, Formatting.Indented, jsonSerializerSettings);
}
//Add headers from old response to new response
foreach (var header in response.Headers)
{
newResponse.Headers.Add(header.Key, header.Value);
}
return newResponse;
}
return response;
}
}
Can anyone help me to provide their guidance in resolving the issue?
Please read the ASP.NET Core Middleware documentation for a better understanding on how middlewares work.
The middleware takes in the next RequestDelegate in its constructor and supports an Invoke method .For example :
public class CommonResponserMiddleware
{
private readonly RequestDelegate _next;
public CommonResponserMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
//process context.Request
await _next.Invoke(context);
//process context.Response
}
}
public static class CommonResponserExtensions
{
public static IApplicationBuilder UseCommonResponser(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CommonResponserMiddleware>();
}
}
And use in Starup.cs:
public void Configure(IApplicationBuilder app) {
//...other configuration
app.UseCommonResponser();
//...other configuration
}
You can also refer to related SO question:
Registering a new DelegatingHandler in ASP.NET Core Web API
How can I wrap Web API responses(in .net core) for consistency?

Setting request headers per request with IHttpClientFactory

I'm using typed clients with IHttpClientFactory. Like this:
// Startup.cs
services.AddHttpClient<MyHttpClient>()
// MyHttpClient.cs
public class MyHttpClient
{
public MyHttpClient(HttpClient client)
{
Client = client;
}
public HttpClient Client { get; }
}
// MyService.cs
public class MyService {
public MyService(MyHttpClient httpClient) {}
public async Task SendRequestAsync(string uri, string accessToken) {
_httpClient.Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
await _httpClient.Client.GetAsync(uri);
}
}
I'm unsure how this works. Will the request headers be set only for this request, or for every subsequent request that is made using this instance of httpClient. How can I set header on a per request basis?
You can use an DelegatingHandler to add an header to each request the HttpClient will make.
public class HeaderHandler: DelegatingHandler
{
public HeaderHandler()
{
}
public HeaderHandler(DelegatingHandler innerHandler): base(innerHandler)
{
}
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("CUSTOM-HEADER","CUSTOM HEADER VALUE");
return await base.SendAsync(request, cancellationToken);
}
}
}
You register the hanlder using :
service.AddTransient<HeaderHandler>()
.AddHttpClient<MyHttpClient>()
.AddHttpMessageHandler<HeaderHandler>();

Redirect outside of the Controllers context in ASP.NET Core

I do not know if this is actually possible, but I think it' s worth a try to find out.
There are maybe other and better patterns (if you know one let me know, I will look them up) to do this, but I'm just curious to know if this is possible.
When you have to call an API you could do it directly from within the controller using the HttpClient like this:
[Authorize]
public async Task<IActionResult> Private()
{
//Example: get some access token to use in api call
var accessToken = await HttpContext.GetTokenAsync("access_token");
//Example: do an API call direcly using a static HttpClient wrapt in a service
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/api/some/endpoint");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await _client.Client.SendAsync(request);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
//Handle situation where user is not authenticated
var rederectUrl = "/account/login?returnUrl="+Request.Path;
return Redirect(rederectUrl);
}
if (response.StatusCode == HttpStatusCode.Forbidden)
{
//Handle situation where user is not authorized
return null;
}
var text = await response.Content.ReadAsStringAsync();
Result result = JObject.Parse(text).ToObject<Result>();
return View(result);
}
When you would do this you'll have to reuse some code over and over again. You could just make a Repository but for some scenarios that would be overkill and you just want to make some quick and dirty API calls.
Now what I want to know is, when we move the logic of setting an Authorization header or handling the 401 and 403 responses outside the controller, how do you redirect or control the controller's action.
Lets say I create a Middleware for the HttpClient like this:
public class ResourceGatewayMessageHandler : HttpClientHandler
{
private readonly IHttpContextAccessor _contextAccessor;
public ResourceGatewayMessageHandler(IHttpContextAccessor context)
{
_contextAccessor = context;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
//Retrieve acces token from token store
var accessToken = await _contextAccessor.HttpContext.GetTokenAsync("access_token");
//Add token to request
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
//Execute request
var response = await base.SendAsync(request, cancellationToken);
//When 401 user is probably not logged in any more -> redirect to login screen
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
//Handle situation where user is not authenticated
var context = _contextAccessor.HttpContext;
var rederectUrl = "/account/login?returnUrl="+context.Request.Path;
context.Response.Redirect(rederectUrl); //not working
}
//When 403 user probably does not have authorization to use endpoint
if (response.StatusCode == HttpStatusCode.Forbidden)
{
//Handle situation where user is not authorized
}
return response;
}
}
We can just do the request like this:
[Authorize]
public async Task<IActionResult> Private()
{
//Example: do an API call direcly using a static HttpClient initiated with Middleware wrapt in a service
var response = await _client.Client.GetAsync("https://example.com/api/some/endpoint");
var text = await response.Content.ReadAsStringAsync();
Result result = JObject.Parse(text).ToObject<Result>();
return View(result);
}
The problem here is that context.Response.Redirect(rederectUrl); does not work. It does not break off the flow to redirect. Is it possible to implement this, and how would you solve this?
Ok since nobody answers my question I've thought about it thoroughly and I came up with the following:
Setup
We have a resource gateway (RG). The RG can return a 401 or 403 meaning that the session is expired (401) or the user does not have sufficient rights (403). We use an access token (AT) to authenticate and authorize our requests to the RG.
authentication
When we get a 401 and we have a refresh token (RT) we want to trigger something that will retrieve a new AT. When there is no RT or the RT is expired we want to reauthenticate the user.
authorization
When we get a 403 we want to show the user that he has no access or something similar like that.
Solution
To handle the above, without making it a hassle for the programmer that uses the API or API wrapper class we can use a Middleware that will specifically handle the Exception thrown by using the API or the API wrapper. The middleware can handle any of the above situations.
Create custom Exceptions
public class ApiAuthenticationException : Exception
{
public ApiAuthenticationException()
{
}
public ApiAuthenticationException(string message) : base(message)
{
}
}
public class ApiAuthorizationException : Exception
{
public ApiAuthorizationException()
{
}
public ApiAuthorizationException(string message) : base(message)
{
}
}
Throw exceptions
Create a wrapper or use the HttpClient middleware to manage the exception throwing.
public class ResourceGatewayMessageHandler : HttpClientHandler
{
private readonly IHttpContextAccessor _contextAccessor;
public ResourceGatewayMessageHandler(IHttpContextAccessor context)
{
_contextAccessor = context;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
//Retrieve acces token from token store
var accessToken = await _contextAccessor.HttpContext.GetTokenAsync("access_token");
//Add token to request
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
//Execute request
var response = await base.SendAsync(request, cancellationToken);
//When 401 user is probably not logged in any more -> redirect to login screen
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new ApiAuthenticationException();
}
//When 403 user probably does not have authorization to use endpoint -> show error page
if (response.StatusCode == HttpStatusCode.Forbidden)
{
throw new ApiAuthorizationException();
}
return response;
}
}
Now you have to setup the HttpClient inside your Startup.cs. There are multiple ways to do this. I advise to use AddTransient to innitiate a wrapper class that uses a HttpClient as a static.
You could do it like this:
public class ResourceGatewayClient : IApiClient
{
private static HttpClient _client;
public HttpClient Client => _client;
public ResourceGatewayClient(IHttpContextAccessor contextAccessor)
{
if (_client == null)
{
_client = new HttpClient(new ResourceGatewayMessageHandler(contextAccessor));
//configurate default base address
_client.BaseAddress = "https://gateway.domain.com/api";
}
}
}
And in your Startup.cs inside the ConfigureServices(IServiceCollection services) you can do:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<ResourceGatewayClient>();
Now you can use the dependency injection in any controller you would like.
Handle the Exceptions
Create something like this middleware (with thanks to this answer):
public class ApiErrorMiddleWare
{
private readonly RequestDelegate next;
public ApiErrorMiddleWare(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
if (exception is ApiAuthenticationException)
{
context.Response.Redirect("/account/login");
}
if (exception is ApiAuthorizationException)
{
//handle not authorized
}
}
Register your middleware
Go to Startup.cs and go to the Configure(IApplicationBuilder app, IHostingEnvironment env) method and add app.UseMiddleware<ApiErrorMiddleWare>();.
This should do it. Currently, I'm creating an example when it is publicly available (after peer review) I'll add a github reference.
I would like to hear some feedback on this solution or an alternative approach.

Categories