ObjectDisposedException / SocketsHttpHandler in .NET 3.1 - c#

I am using asp.net core in one of my projects and I am making some https requests with a client certificate. To achieve this, I created a typed http client and injected it in my startup.cs like the following:
services.AddHttpClient<IClientService, ClientService>(c =>
{
}).ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls | SslProtocols.Tls11;
handler.ClientCertificates.Add(clientCertificate);
handler.ServerCertificateCustomValidationCallback = delegate { return true; };
return handler;
}
);
My service is implemented like the following:
public class ClientService : IClientService
{
public HttpClient _httpClient { get; private set; }
private readonly string _remoteServiceBaseUrl;
private readonly IOptions<AppSettings> _settings;
public ClientService(HttpClient httpClient, IOptions<AppSettings> settings)
{
_httpClient = httpClient;
_httpClient.Timeout = TimeSpan.FromSeconds(60);
_settings = settings;
_remoteServiceBaseUrl = $"{settings.Value.ClientUrl}"; /
}
async public Task<Model> GetInfo(string id)
{
var uri = ServiceAPI.API.GetOperation(_remoteServiceBaseUrl, id);
var stream = await _httpClient.GetAsync(uri).Result.Content.ReadAsStreamAsync();
var cbor = CBORObject.Read(stream);
return JsonConvert.DeserializeObject<ModelDTO>(cbor.ToJSONString());
}
}
In my calling class, I am using this code:
public class CommandsApi
{
IClientService _clientService;
public CommandsApi( IclientService clientService)
: base(applicationService, loggerFactory)
{
_clientService = clientService;
_loggerfactory = loggerFactory;
}
public async Task<IActionResult> Post(V1.AddTransaction command)
{
var result = await _clientService.GetInfo(command.id);
}
}
It works just fine however after sending many requests I am receiving the foloowing error:
Cannot access a disposed object. Object name: 'SocketsHttpHandler'.
at System.Net.Http.SocketsHttpHandler.CheckDisposed()
at System.Net.Http.SocketsHttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.DiagnosticsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
I tried some solutions that I found in previous issues (asp.net core github )and stackoverflow but they did not work.
Any idea ? Thank you

I suspect it's caused by resources not being disposed properly. There's an unnecessary call to .Result and you create a stream, but you don't dispose of it. If you use using statement, then the stream should be disposed. (you can always call stream.dispose() but I wouldn't recommend it).
var stream = await _httpClient.GetAsync(uri).Result.Content.ReadAsStreamAsync();
I've not run this, but consider:
public async Task<Model> GetInfo(string id)
{
var uri = ServiceAPI.API.GetOperation(_remoteServiceBaseUrl, id);
var response = await _httpClient.GetAsync(uri);
using (var stream = await response.Content.ReadAsStreamAsync())
{
var cbor = CBORObject.Read(stream);
return JsonConvert.DeserializeObject<ModelDTO>(cbor.ToJSONString());
}
}

So after many tests I followed #Greg advice and implemented a class inheriting from HttpClientHandler and injected it like the following:
services.AddTransient<MyHttpClientHandler>();
services.AddHttpClient<IClientService, ClientService>().
ConfigurePrimaryHttpMessageHandler<MyHttpClientHandler>();
this solved my problem.
Thank you #Greg for the link
How to use ConfigurePrimaryHttpMessageHandler generic

This is not what the original question was for, but I stumbled onto this question while searching for a similar issue.
When adding an HttpMessageHandler to the HttpClient, make sure to always return a new HttpMessageHandler instance and not re-use the same HttpMessageHandler instance.
builder.Services
.AddHttpClient<IClient>()
.ConfigurePrimaryHttpMessageHandler(() =>
{
// Always return a new instance!!
return new HttpClientHandler();
});
The document of the ConfigurePrimaryHttpMessageHandler extension method states that
The configureHandler delegate should return a new instance of the message handler each time it is invoked.
See this github answer for more information
https://github.com/dotnet/runtime/issues/27610#issuecomment-433101757

Related

Scoped service creating two different instances for a request

I'm fairly new to Asp.Net core 6 and am working on an GraphQL API that receives a bearer token in the request. The API then invokes another Web API and passes the same bearer token in the header. Below is what my code looks like-
Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<HeaderHandler>();
builder.Services.AddScoped<TokenContainer>();
//builder.Services.AddScoped<IFooGateway, FooGateway>();
builder.Services.AddHttpClient<IFooGateway, FooGateway>((c) =>
{
c.BaseAddress = new Uri(builder.Configuration["FooApiUrl"]);
})
.AddHttpMessageHandler<HeaderHandler>();
builder.Services.AddTransient<GraphApiService>();
var app = builder.Build();
app.UseMiddleware<HeaderMiddleware>();
app.MapGraphQL();
app.Run();
HeaderMiddleware.cs
public class HeaderMiddleware
{
//TokenContainer _tokenContainer;
private readonly RequestDelegate _requestDelegate;
public HeaderMiddleware()
{
}
public HeaderMiddleware(RequestDelegate requestDelegate)
{
_requestDelegate = requestDelegate;
//_tokenContainer = tokenContainer;
}
public async Task InvokeAsync(HttpContext context, TokenContainer tokenContainer)
{
var header = context.Request.Headers.Authorization;
tokenContainer.SetToken(header);
await _requestDelegate(context);
}
TokenContainer.cs:
public class TokenContainer
{
public string BearerToken { get; private set; }
public void SetToken(string token) => BearerToken = token;
}
HeaderHandler.cs:
public class HeaderHandler : DelegatingHandler
{
TokenContainer _tokenContainer;
public HeaderHandler()
{
}
public HeaderHandler(TokenContainer tokenContainer)
{
_tokenContainer = tokenContainer;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// for every request sent via the http client, intercept & add the bearer token header.
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", _tokenContainer.BearerToken);
// continue with request pipeline
return await base.SendAsync(request, cancellationToken);
}
}
FooGateway.cs:
public class FooGateway : IFooGateway
{
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
private readonly string _context = String.Empty;
public FooGateway(HttpClient httpClient, IConfiguration configuration)
{
_configuration = configuration;
_context = configuration["FooContext"];
_httpClient = httpClient;
}
public void DoSomething()
{
_httpClient.PostAsync("/blabla");
}
}
So, the idea was that the bearer token for every incoming request will be stored in a class called TokenContainer and the HttpHandler will add it to all the outgoing requests.
However, what is happening is that the token is stored in the TokenContainer but the HeaderHandler gets a different instance of TokenContainer in its constructor with its BearerToken property set to null.
Can someone please explain why the same instance of TokenContainer from the middleware is not being passed into the HeaderHandler?
The issue you are seeing is because the lifetime of the HttpMessageHandler is not the same as the lifetime of the request: usually, the same handler will be reused across many requests and be controlled separately on expiration timers and such.
You should not expect that a service injected into your message handler will be the same object that is injected outside it when it is registered as scoped.
https://andrewlock.net/understanding-scopes-with-ihttpclientfactory-message-handlers/#scope-duration-in-ihttpclientfactory
As the article suggests, to use the same scoped instance as you do outside the handler, you have to rely on IHttpContextAccessor to access the current HttpContext and fetch the service from there. So your handler implementation would look something like this:
public class HeaderHandler : DelegatingHandler
{
IHttpContextAccessor _httpContextAccessor;
public HeaderHandler()
{
}
public HeaderHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var tokenContainer = _httpContextAccessor
.HttpContext
.RequestServices
.GetRequiredService<TokenContainer>();
// for every request sent via the http client, intercept & add the bearer token header.
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", tokenContainer.BearerToken);
// continue with request pipeline
return await base.SendAsync(request, cancellationToken);
}
}
This should make sure that the TokenContainer instance is the same across your current request and http calls.
Remember that to add this functionality you need to add the accessor like this:
services.AddHttpContextAccessor();

HttpContext Header

I have created this class for getting the Header value from requests.
public class AuthenticationHeader
{
private static IHttpContextAccessor _httpContextAccessor;
public AuthenticationHeader(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string AuthHeader => _httpContextAccessor.HttpContext?.Request.Headers["Authorization"];
}
and that I have registered that in my startup.cs like this
services.AddSingleton<AuthenticationHeader>();
And its been injected into my other classes like this.
public BaseClient(HttpClient client, ILogger<BaseClient> logger, AuthenticationHeader authHeader)
{
_client = client;
client.BaseAddress = new Uri("yrl");
client.DefaultRequestHeaders.Add("Accept", "application/json");
_logger = logger;
AuthHeader = authHeader;
}
Now as I have registered that as Singleton. So when call my Api for first time and provide the Authorization value in header the api is called successfully but the issue is when i pass empty Authorization header it still call's api successfully as it is storing old header value due to Singleton. How can I fix this? Is there any otherways to do what I am doing.
Try using HttpClientFactory, that was added Asp.Net Core 2.1, in conjunction with HttpMessageHandler to achieve what you are trying to do.
You can register the HttpClient in ConfigureServices method
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<BaseClient>(client =>
{
client.BaseAddress = new Uri("yrl");
client.DefaultRequestHeaders.Add("Accept", "application/json");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
}
With the above code in place, your BaseClient will receive the HttpClient instance via DI.
In order to validate/inspect the AuthHeader you can configure the HttpMessageHandler for the registered HttpClient. The code for the message handler is simple like below:
public class AuthHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (!request.Headers.Contains("Authorization"))
{
return new HttpResponseMessage(HttpStatusCode.Forbidden)
{
Content = new StringContent("No Authorization header is present")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
In order to register the above handler, your code will look like below:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<AuthHeaderHandler>();
services.AddHttpClient<BaseClient>(client =>
{
//code omitted for brevity
...
})
.AddHttpMessageHandler<AuthHeaderHandler>();
}
You can inject whatever you need inside the message handler if needed. However, no need to inject the IHttpContextAccessor in the BaseClient. To read more about HttpClientFactory and HttpMessageHandlers please see this link and this. I hope this helps.
UPDATED ANSWER
Please have a look at the more concrete example of HttpMessageHandler that uses the IHttpContextAccessor and modifies the HttpRequestMessage i.e. adds the Authorization header before the call is made. You can modify the logic as per your need.
public class AuthHeaderHandler : DelegatingHandler
{
private readonly HttpContext _httpContext;
public AuthHeaderHandler(IHttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (_httpContext != null)
{
var accessToken = await _httpContext.GetTokenAsync(TokenKeys.Access);
if (!string.IsNullOrEmpty(accessToken))
{
// modify the request header with the new Authorization token
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
}
}
return await base.SendAsync(request, cancellationToken);
}
}
UPDATED ANSWER 2
Please have a look at the simple solution that I have uploaded to GitHub. The solution is even simpler than I originally suggested. As you are not integrating any identity-based Authentication/Authorization, you can simply use a CustomActionFilter, I called it ValidateAuthHeader, to check if the AuthHeader is present or not and return the usual 403 if absent.
Within the ValidateAuthHeader, I have utilised the middleware code that you posted earlier. You can then simply add this attribute on the ActionMethods or Controllers which require this check.
Please have a look at the DataController and ValuesController. The DataController will receive the typed HttpClient that will be used to call the values endpoint. ValidateAuthHeader is present on the GetValues and will check for the AuthHeader. If it's absent it will generate the error.
[Route("api/[controller]")]
[ApiController]
public class DataController : ControllerBase
{
private readonly MyHttpClient _client;
public DataController(MyHttpClient client)
{
_client = client;
}
[ValidateAuthHeader]
public async Task<IActionResult> GetValues()
{
var response = await _client.GetAsync("api/values");
var contents = await response.Content.ReadAsStringAsync();
return new ContentResult
{
Content = contents,
ContentType = "application/json",
StatusCode = 200
};
}
}
The rest of the flow is the same as I originally suggested. The call will be passed through the AuthHeaderHandler which is an HttpMessageHandler for the registered MyHttpClient. Please have a look at the Startup.cs.
The handler will retrieve the HttpContext via HttpContextAccessor and will check for the AuthHeader. If present, it will add it to the RequestMessage parameter.
I hope this helps. Feel free to ask any questions that you may have.
Setting Auth Header without using HttpMessageHandler
Modify the MyHttpClient and add a public method called SetAuthHeader
public class MyHttpClient
{
private readonly HttpClient _httpClient;
public MyHttpClient(HttpClient client)
{
_httpClient = client;
}
public void SetAuthHeader(string value)
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", value);
}
}
Then call this method in your action method as you will have the AuthHeader in the HttpContext.Request at that point
[ValidateAuthHeader]
public async Task<IActionResult> GetValues()
{
var authHeader = Request.Headers["Authorization"];
_client.SetAuthHeader(authHeader.First());
var response = await _client.GetAsync("api/values");
var contents = await response.Content.ReadAsStringAsync();
return new ContentResult
{
Content = contents,
ContentType = "application/json",
StatusCode = 200
};
}
Remove the AuthHeaderHandler registration and delete the AuthHeaderHandler.

Unit Testing methods within which extension methods are used for external resources

I need to Unit test RequestToken method that uses HttpClient and extension method RequestPasswordTokenAsync.
I have got a null reference exception even when a delegatingHandler is passed to the HttpClient.
var delegatingHandler = new DelegatingHandlerStub(false);
var httpClient = new HttpClient(delegatingHandler);
var tokenServices = new TokenServices(httpClient)
tokenServices.RequestToken(passwordTokenRequest); //exception
public class TokenServices : ITokenServices
{
private readonly HttpClient _httpClient;
public TokenServices(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<TokenResponse> RequestToken(PasswordTokenRequest request)
{
var response = await _httpClient.RequestPasswordTokenAsync(request);
}
}
public class DelegatingHandlerStub : DelegatingHandler
{
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
public DelegatingHandlerStub(bool toThrowException)
{
_handlerFunc = (request, cancellationToken) =>
{
if (toThrowException)
throw new Exception();
return Task.FromResult(request.CreateResponse(HttpStatusCode.OK));
};
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return _handlerFunc(request, cancellationToken);
}
}
namespace IdentityModel.Client
{
public static class HttpClientTokenRequestExtensions
{
public static Task<TokenResponse> RequestPasswordTokenAsync(this HttpMessageInvoker client, PasswordTokenRequest request, CancellationToken cancellationToken = default(CancellationToken));
}
}
I have to write a wrapper around HttpClient.
If there are a better solution, please post it here

ObjectDisposedException when reading requests HttpContent in test

I'm integrating a 3rd part API using TDD, and so I am implementing a HttpClient wrapper interface that exposes the possible api calls and so on.
I want to test that the correct payload was sent in a post method, but when I try to read the string content from my injected fake HttpMessageHandler I get an ObjectDisposedException. Is there a better way to test this?
Test code:
[Fact]
public async void PostSignupRequest_RequestSent_PostedSerializedRequestAsContent()
{
var client = MakeOnboardingClient();
_fakeJsonSerializer.SerializedResult = "some json";
await client.PostSignupRequest(_someSignupRequest);
Assert.Equal("some json", await _fakeMessageHandler.Request.Content.ReadAsStringAsync());
}
My HttpMessageHandler spy/test double:
public class FakeHttpMessageHandler : HttpMessageHandler
{
public HttpRequestMessage Request;
public string ResponseContent = string.Empty;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Request = request;
return await Task.FromResult(new HttpResponseMessage
{
Content = new StringContent(ResponseContent)
});
}
}
Production code:
public async Task<SignupRequestResponse> PostSignupRequest(SignupRequest request)
{
var json = _jsonSerializer.Serialize(request);
await _httpClient.PostAsync(/* url */, new StringContent(json));
return null;
}
I've found a fix now. In my HttpMessageHandler fake I don't just save the Request now, I also explicitly save the content string (which can be extracted at that point since the HttpClient hasn't disposed the request yet). My fake now looks like this:
public class FakeHttpMessageHandler : HttpMessageHandler
{
public HttpRequestMessage Request;
public string LastRequestString = string.Empty;
public string ResponseContent = string.Empty;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request.Content != null) // needed this to prevent some NPEs in other tests, YMMV
{
LastRequestString = await request.Content.ReadAsStringAsync();
}
Request = request;
return await Task.FromResult(new HttpResponseMessage
{
Content = new StringContent(ResponseContent)
});
}
}

How can I test a custom DelegatingHandler in the ASP.NET MVC 4 Web API?

I've seen this question come up in a few places, and not seen any great answers. As I've had to do this myself a few times, I thought I'd post my solution. If you have anything better, please post.
N.B. This is using ASP.NET MVC 4 Beta 2 version of Web API - future versions may change!
Update: This still works in ASP.NET MVC 4 RC
In this approach, I create a TestHandler and set it as the InnerHandler property of the handler under test.
The handler under test can then be passed to an HttpClient - this may seem unintuitive if you are writing a server-side handler, but this is actually a great light-weight way to test a handler - it will be called in the same way it would in a server.
The TestHandler will just return an HTTP 200 by default, but it's constructor accepts a function you can use to make asserts about the request message passed
in from the handler under test. Finally you can make asserts on the result of the SendAsync call from the client.
Once everything is set up, call SendAsync on the client instance to invoke your handler. The request will be passed into your handler, it will pass this on to the TestHandler (assuming it passes the call on) which will then return a response back to your handler.
The test handler looks like this:
public class TestHandler : DelegatingHandler
{
private readonly Func<HttpRequestMessage,
CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
public TestHandler()
{
_handlerFunc = (r, c) => Return200();
}
public TestHandler(Func<HttpRequestMessage,
CancellationToken, Task<HttpResponseMessage>> handlerFunc)
{
_handlerFunc = handlerFunc;
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
return _handlerFunc(request, cancellationToken);
}
public static Task<HttpResponseMessage> Return200()
{
return Task.Factory.StartNew(
() => new HttpResponseMessage(HttpStatusCode.OK));
}
}
Example usage with an imagined MyHandler under test. Uses NUnit for the asserts.:
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
httpRequestMessage.Headers.Add("username", "test");
var handler = new MyHandler()
{
InnerHandler = new TestHandler((r,c) =>
{
Assert.That(r.Headers.Contains("username"));
return TestHandler.Return200();
})
};
var client = new HttpClient(handler);
var result = client.SendAsync(httpRequestMessage).Result;
Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.OK));
The default behaviour of TestHandler is probably fine for many tests and makes the code simpler. The setup of the handler under test then looks like this:
var handler = new MyHandler();
handler.InnerHandler = new TestHandler();
I like this approach because it keeps all the assertions in the test method, and the TestHandler is very reusable.
I was just looking for the same thing but came up with a more concise approach that didn't use http client. I wanted a test to assert the message handler consumed a mocked logging component. I didn't really need the inner handler to function, just to "stub" it out to satisfy the unit test. Works for my purpose :)
//ARRANGE
var logger = new Mock<ILogger>();
var handler= new ServiceLoggingHandler(logger.Object);
var request = ControllerContext.CreateHttpRequest(Guid.NewGuid(), "http://test",HttpMethod.Get);
handler.InnerHandler = new Mock<HttpMessageHandler>(MockBehavior.Loose).Object;
request.Content = new ObjectContent<CompanyRequest>(Company.CreateCompanyDTO(), new JsonMediaTypeFormatter());
var invoker = new HttpMessageInvoker(handler);
//ACT
var result = invoker.SendAsync(request, new System.Threading.CancellationToken()).Result;
//ASSERT
<Your assertion>
I also found this answer because i have my custom handler and i want to test it
We are using NUnit and Moq, so i think my solution can be helpful for someone
using Moq;
using Moq.Protected;
using NUnit.Framework;
namespace Unit.Tests
{
[TestFixture]
public sealed class Tests1
{
private HttpClient _client;
private HttpRequestMessage _httpRequest;
private Mock<DelegatingHandler> _testHandler;
private MyCustomHandler _subject;//MyCustomHandler inherits DelegatingHandler
[SetUp]
public void Setup()
{
_httpRequest = new HttpRequestMessage(HttpMethod.Get, "/someurl");
_testHandler = new Mock<DelegatingHandler>();
_subject = new MyCustomHandler // create subject
{
InnerHandler = _testHandler.Object //initialize InnerHandler with our mock
};
_client = new HttpClient(_subject)
{
BaseAddress = new Uri("http://localhost")
};
}
[Test]
public async Task Given_1()
{
var mockedResult = new HttpResponseMessage(HttpStatusCode.Accepted);
void AssertThatRequestCorrect(HttpRequestMessage request, CancellationToken token)
{
Assert.That(request, Is.SameAs(_httpRequest));
//... Other asserts
}
// setup protected SendAsync
// our MyCustomHandler will call SendAsync internally, and we want to check this call
_testHandler
.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", _httpRequest, ItExpr.IsAny<CancellationToken>())
.Callback(
(Action<HttpRequestMessage, CancellationToken>)AssertThatRequestCorrect)
.ReturnsAsync(mockedResult);
//Act
var actualResponse = await _client.SendAsync(_httpRequest);
//check that internal call to SendAsync was only Once and with proper request object
_testHandler
.Protected()
.Verify("SendAsync", Times.Once(), _httpRequest, ItExpr.IsAny<CancellationToken>());
// if our custom handler modifies somehow our response we can check it here
Assert.That(actualResponse.IsSuccessStatusCode, Is.True);
Assert.That(actualResponse, Is.EqualTo(mockedResult));
//...Other asserts
}
}
}
I created the following for testing DelegatingHandlers. It is useful for handlers that use the HttpRequestMessage.DependencyScope to resolve dependencies using your favorite IoC framework e.g. a WindsorDependencyResolver with a WindsorContainer:
public class UnitTestHttpMessageInvoker : HttpMessageInvoker
{
private readonly HttpConfiguration configuration;
public UnitTestHttpMessageInvoker(HttpMessageHandler handler, IDependencyResolver resolver)
: base(handler, true)
{
this.configuration = new HttpConfiguration();
configuration.DependencyResolver = resolver;
}
[DebuggerNonUserCode]
public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
request.Properties["MS_HttpConfiguration"] = this.configuration;
return base.SendAsync(request, cancellationToken);
}
}
Another alternative could be stubbing
public class TestingHandlerStub : DelegatingHandler
{
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
public TestingHandlerStub(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handlerFunc)
{
_handlerFunc = handlerFunc;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return _handlerFunc(request, cancellationToken);
}
}
And this is how to combine :
var handler = new YourCustomHandler()
{
InnerHandler = new TestingHandlerStub((r, c) =>
{
return Task.FromResult(httpResponseMessage);
})
};
var client = new HttpClient(handler);

Categories

Resources