I'm working on accessing REST Api from Xamarin forms. I have created an interface in portable class like this by adding
public interface IRestService<T>
{
void Post(T item, string resourceURL);
}
public interface IRepository
{
}
and in Drod Project I'm implemented the interface like this.
[assembly:Xamarin.Forms.Dependency(typeof(RestOperationsDroid))]
namespace VLog.Droid.DependencyServices
{
public class RestOperationsDroid : IRestService<IRepository>
{
private const string BaseURL = "http://127.0.0.1/logger/v1/";
HttpClient client;
public RestOperationsDroid()
{
client = new HttpClient();
client.MaxResponseContentBufferSize = 256000;
}
public async void Post(object item, string url)
{
var uri = new Uri(string.Format(BaseURL + url));
var jsoncontent = new StringContent(JsonConvert.SerializeObject(item), Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
response = await client.PostAsync(uri, jsoncontent);
if (response.IsSuccessStatusCode)
{
//Debug(#" TodoItem successfully saved.");
}
else
{
//throw new Exception("Failed to Post data");
}
}
}
}
Calling in Xamarin.Forms (PCL project)
DependencyService.Get<IRestService<Log>>().Post(_logitem, "sync");
I'm getting an error says :
Object reference not set to an instance of an object.
What went wrong in this implementation ?
You are trying to resolve an IRestService<Log> from the DependencyService, while your class implements IRestService<IRepository>. These are different types. You should implement a class that implements the specific type you want for IRestService<T>.
Related
I have an ASP.NET MVC application which invokes an ASP.NET Web API REST Service each time a button is pressed in the UI.
Each time this button is pressed below DumpWarehouseDataIntoFile method is executed.
public class MyClass
{
private static HttpClient client = new HttpClient();
public async Task DumpWarehouseDataIntoFile(Warehouse myData, string path, string filename)
{
try
{
//Hosted web API REST Service base url
string Baseurl = "http://XXX.XXX.XX.X:YYYY/";
//using (var client = new HttpClient()) --> I have declared client as an static variable
//{
//Passing service base url
client.BaseAddress = new Uri(Baseurl);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Serialize parameter to pass to the asp web api rest service
string jsonParam = Newtonsoft.JsonConvert.SerializeObject(myData);
//Sending request to find web api REST service resource using HttpClient
var httpContent = new StringContent(jsonParam, Encoding.UTF8, "application/json");
HttpResponseMessage Res = await client.PostAsync("api/Warehouse/DumpIntoFile", httpContent);
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
// Some other sftuff here
}
//}
}
catch (Exception ex)
{
// Do some stuff here
} // End Try
} // End DumpWarehouseDataIntoFile method
} // End class
Warehouse class object:
public class Warehouse
{
public DataTable dt { get; set; }
public string Filepath { get; set; }
}
I have found in this post that pattern:
using (var myClient = new HttpClient())
{
}
is not recommended to be used since it leads to socket exhaustion (System.Net.Sockets.SocketException). There it is recommended to use HttpClient as static variable and reuse it as it helps to reduce waste of sockets. So I have used a static variable.
The problem with this approach (in my scenario) is that it only works first button is pressed, next times button is pressed and DumpWarehouseDataIntoFile method is executed, below exception is thrown:
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: This instance has already started
one or more requests. Properties can only be modified before sending
the first request.
As error says, properties like base address, etc. can only be modified once before sending the first request.
I have googled and found some solutions proposed:
First solution
So it seems like singleton pattern would be a good option, as proposed here. Below the singleton proposed by Alper:
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
//You need to install package Newtonsoft.Json > https://www.nuget.org/packages/Newtonsoft.Json/
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public class MyApiClient : IDisposable
{
private readonly TimeSpan _timeout;
private HttpClient _httpClient;
private HttpClientHandler _httpClientHandler;
private readonly string _baseUrl;
private const string ClientUserAgent = "my-api-client-v1";
private const string MediaTypeJson = "application/json";
public MyApiClient(string baseUrl, TimeSpan? timeout = null)
{
_baseUrl = NormalizeBaseUrl(baseUrl);
_timeout = timeout ?? TimeSpan.FromSeconds(90);
}
public async Task<string> PostAsync(string url, object input)
{
EnsureHttpClientCreated();
using (var requestContent = new StringContent(ConvertToJsonString(input), Encoding.UTF8, MediaTypeJson))
{
using (var response = await _httpClient.PostAsync(url, requestContent))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
}
public async Task<TResult> PostAsync<TResult>(string url, object input) where TResult : class, new()
{
var strResponse = await PostAsync(url, input);
return JsonConvert.DeserializeObject<TResult>(strResponse, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
public async Task<TResult> GetAsync<TResult>(string url) where TResult : class, new()
{
var strResponse = await GetAsync(url);
return JsonConvert.DeserializeObject<TResult>(strResponse, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
public async Task<string> GetAsync(string url)
{
EnsureHttpClientCreated();
using (var response = await _httpClient.GetAsync(url))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
public async Task<string> PutAsync(string url, object input)
{
return await PutAsync(url, new StringContent(JsonConvert.SerializeObject(input), Encoding.UTF8, MediaTypeJson));
}
public async Task<string> PutAsync(string url, HttpContent content)
{
EnsureHttpClientCreated();
using (var response = await _httpClient.PutAsync(url, content))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
public async Task<string> DeleteAsync(string url)
{
EnsureHttpClientCreated();
using (var response = await _httpClient.DeleteAsync(url))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
public void Dispose()
{
_httpClientHandler?.Dispose();
_httpClient?.Dispose();
}
private void CreateHttpClient()
{
_httpClientHandler = new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
};
_httpClient = new HttpClient(_httpClientHandler, false)
{
Timeout = _timeout
};
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(ClientUserAgent);
if (!string.IsNullOrWhiteSpace(_baseUrl))
{
_httpClient.BaseAddress = new Uri(_baseUrl);
}
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeJson));
}
private void EnsureHttpClientCreated()
{
if (_httpClient == null)
{
CreateHttpClient();
}
}
private static string ConvertToJsonString(object obj)
{
if (obj == null)
{
return string.Empty;
}
return JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
private static string NormalizeBaseUrl(string url)
{
return url.EndsWith("/") ? url : url + "/";
}
}
Usage
using (var client = new MyApiClient("http://localhost:8080"))
{
var response = client.GetAsync("api/users/findByUsername?username=alper").Result;
var userResponse = client.GetAsync<MyUser>("api/users/findByUsername?username=alper").Result;
}
The problem I see here is that if you call above code many times (in my case would be each time I press the button on the UI and I call DumpWarehouseDataIntoFile method), you create and instance of MyApiClient each time and therefore a new instance of HttpClient is created and I want to reuse HttpClient, not to make many instances of it.
Second solution
Creating a kind of factory as proposed here by Nico. Below the code he proposes:
public interface IHttpClientFactory
{
HttpClient CreateClient();
}
public class HttpClientFactory : IHttpClientFactory
{
static string baseAddress = "http://example.com";
public HttpClient CreateClient()
{
var client = new HttpClient();
SetupClientDefaults(client);
return client;
}
protected virtual void SetupClientDefaults(HttpClient client)
{
client.Timeout = TimeSpan.FromSeconds(30); //set your own timeout.
client.BaseAddress = new Uri(baseAddress);
}
}
Usage
public HomeController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
readonly IHttpClientFactory _httpClientFactory;
public IActionResult Index()
{
var client = _httpClientFactory.CreateClient();
//....do your code
return View();
}
Here again you create a new instance of HttpClient each time you call CreateClient. You do not reuse HttpClient object.
Third Solution
Making HTTP requests using IHttpClientFactory as explained here.
The problem is that it is only available for .NET Core, not standard ASP.NET Framework, though it seems it is available by installing this nuget package. It seems like it automatically manages efficiently HttpClient instances and I would like to apply it to my scenario. I want to avoid to
reinvent the wheel.
I have never used IHttpClientFactory and I have no idea on how to use it: configure some features like base address, set request headers, create an instance of HttpClient and then invoke PostAsync on it passing as parameter the HttpContent.
I think this is the best approach so could someone tell me the necessary steps I need to do in order to make the same things I do in DumpWarehouseDataIntoFile method but using IHttpClientFactory? I am a bit lost, I do not know how to apply IHttpClientFactory to do the same as I do within DumpWarehouseDataIntoFile method.
Any others solutions not proposed here and also some code snippets will be highly appreciated.
HttpClient
The HttpClient can throw InvalidOperationException in the following cases:
When the BaseAddress setter is called after a request has been sent out
When the Timeout setter is called after a request has been sent out
When the MaxResponseContentBufferSize setter is called after a request has been sent out
When an operation has already started and resend was requested
In order to avoid these you can set the first two on per request level, for example:
CancellationTokenSource timeoutSource = new CancellationTokenSource(2000);
await httpClient.GetAsync("http://www.foo.bar", timeoutSource.Token);
HttpClientFactory
You can use the IHttpClientFactory in .NET Framework with the following trick:
AddHttpClient registers the DefaultHttpClientFactory for IHttpClientFactory
Then you can retrieve it from the DI container
var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
container.RegisterInstance(serviceProvider.GetService<IHttpClientFactory>());
container.ContainerScope.RegisterForDisposal(serviceProvider);
This sample uses SimpleInjector but the same concept can be applied for any other DI framework.
I'm not sure but will what happen if you move this lines to constructor:
//Passing service base url
client.BaseAddress = new Uri(Baseurl);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
I think that re-initialization is problem.
Better to add the request url and the headers at the message. Don't use httpClient.BaseAddress or httpClient.DefaultRequestHeaders unless you have a default requirement.
HttpRequestMessage msg = new HttpRequestMessage {
Method = HttpMethod.Put,
RequestUri = new Uri(url),
Headers = httpRequestHeaders;
};
httpClient.SendAsync(msg);
It works well for reusing the HttpClient for many requests
I was trying to build a generic HTTP service in my project (c# with .net core 2.1), and I have done it as per the below snippet HttpService.
I also started using it by calling it from my business logic class which uses this generic PostAsync method to post an HTTP call to a 3rd party with a content in body. It works perfectly.
But, when I tried to test it, I failed!
Actually when I tried debugging (testing mode), I get null response when the debugger comes to this line var result = await _httpService.PostAsync("https://test.com/api", content); in business class Processor even with fake objects and mocks, although it works normally in debugging mode without testing/mocking.
HTTP service:
public interface IHttpService
{
Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content);
}
public class HttpService : IHttpService
{
private readonly IHttpClientFactory _httpClientFactory;
public HttpService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content)
{
var httpClient = _httpClientFactory.CreateClient();
httpClient.Timeout = TimeSpan.FromSeconds(3);
var response = await httpClient.PostAsync(requestUri, content).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
return response;
}
}
Business class:
public class Processor : IProcessor
{
private readonly IHttpService _httpService;
public Processor() { }
public Processor(IHttpService httpService, IAppSettings appSettings)
{
_httpService = httpService;
}
public async Task<HttpResponseMessage> PostToVendor(Order order)
{
// Building content
var json = JsonConvert.SerializeObject(order, Formatting.Indented);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// HTTP POST
var result = await _httpService.PostAsync("https://test.com/api", content); // returns null during the test without stepping into the method PostAsyn itself
return result;
}
}
Test class:
public class MyTests
{
private readonly Mock<IHttpService> _fakeHttpMessageHandler;
private readonly IProcessor _processor; // contains business logic
private readonly Fixture _fixture = new Fixture();
public FunctionTest()
{
_fakeHttpMessageHandler = new Mock<IHttpService>();
_processor = new Processor(_fakeHttpMessageHandler.Object);
}
[Fact]
public async Task Post_To_Vendor_Should_Return_Valid_Response()
{
var fakeHttpResponseMessage = new Mock<HttpResponseMessage>(MockBehavior.Loose, new object[] { HttpStatusCode.OK });
var responseModel = new ResponseModel
{
success = true,
uuid = Guid.NewGuid().ToString()
};
fakeHttpResponseMessage.Object.Content = new StringContent(JsonConvert.SerializeObject(responseModel), Encoding.UTF8, "application/json");
var fakeContent = _fixture.Build<DTO>().Create(); // DTO is the body which gonna be sent to the API
var content = new StringContent(JsonConvert.SerializeObject(fakeContent), Encoding.UTF8, "application/json");
_fakeHttpMessageHandler.Setup(x => x.PostAsync(It.IsAny<string>(), content))
.Returns(Task.FromResult(fakeHttpResponseMessage.Object));
var res = _processor.PostToVendor(fakeContent).Result;
Assert.NotNull(res.Content);
var actual = JsonConvert.SerializeObject(responseModel);
var expected = await res.Content.ReadAsStringAsync();
Assert.Equal(expected, actual);
}
}
Your problem is in mock set up:
_fakeHttpMessageHandler.Setup(x => x.PostAsync(It.IsAny<string>(), content))
.Returns(Task.FromResult(fakeHttpResponseMessage.Object));
Second parameter for PostAsync method expected to be content, but since StringContent is a reference type, content you setup in mock is different from content you creating in processor. If you change it to next one, it should work as you expect:
_fakeHttpMessageHandler.Setup(x => x.PostAsync(It.IsAny<string>(), It.IsAny<StringContent>()))
.Returns(Task.FromResult(fakeHttpResponseMessage.Object));
P.S. null response to PostAsync means that method has default setup, which means that it will return default value
I have X controllers that use a API site (WebApi). I have created an ApiHelper class. Which I use in these controllers. Now my question is this. Can I make this ApiHelper a static class? I think I can because the httpClient is instanced. Or do I overlook something, and does it need to be an instanced ApiHelper. (the use of static still confuses me sometimes). Example code below.
public class HomeController : Controller
{
public async Task<string> VersionDemo()
{
var response = await ApiHelper.Call("/api/config/version");
var data = response.Content.ReadAsStringAsync();
var res = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(data.Result);
return res;
}
}
public class ConfigController : Controller
{
private async Task<List<ConfigSetting>> GetGeneralConfigurationDemo()
{
var generalConfiguration = new List<ConfigSetting>();
var response = await ApiHelper.Call("api/configuration/GetGeneralConfiguration");
var data = response.Content.ReadAsStringAsync();
generalConfiguration = JsonConvert.DeserializeObject<List<ConfigSetting>>(data.Result);
return generalConfiguration;
}
}
public static class ApiHelper
{
public static async Task<HttpResponseMessage> Call(string url)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var baseAdress = System.Configuration.ConfigurationManager.AppSettings["ApiBaseAddress"];
string apiUrl = baseAdress + url;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(apiUrl);
return response;
}
}
}
Make base controller and hide http client as protected thing.
public abstract class BaseController : Controller
{
protected ApiHelper Api { get; set; }
}
Then derive your controllers from BaseController
public class ConfigController : BaseController {}
public class HomeController : BaseController {}
Note : try not to use static classes cause they make your heap littered. They are allocated in "high-frequency" heap, which is never garbage collected.
There would be no problem to leave your class static as the HttpClient stays on the method scope and thus each call to your static method will use a different HttpClient. It would not be safe if you used a static member (field or property) as it would be shared by all the callers and you would need to synchronize the access (for a multi thread usage).
After reading (httpClient your are doing it wrong , singleton pattern) and subsequently testing. I ended up using the following code. Main goal is one httpClient application wide and avoid socket exhaustion.
In my controllers where I'm in need of a httpClient I use the HttpClientSingleton.Instance see below.
And here is a BaseController you can inherit from in your controllers that are going to use your API.
public class BaseController : Controller
{
public readonly string ApiBaseAdress = System.Configuration.ConfigurationManager.AppSettings["ApiBaseAddress"];
public BaseController()
{
//Set as needed Servicepoint settings
//string SecurityProtocolTypeFromConfig = System.Configuration.ConfigurationManager.AppSettings["SecurityProtocolType"];
//SecurityProtocolType fromConfig;
//Enum.TryParse(SecurityProtocolTypeFromConfig, out fromConfig);
//ServicePointManager.SecurityProtocol = fromConfig;
//possible ServicePoint setting needed in some cases.
//ServicePointManager.Expect100Continue = false;
//ServicePointManager.MaxServicePointIdleTime = 2000;
//ServicePointManager.SetTcpKeepAlive(false, 1, 1);
}
}
And here is the HttpClientSingleton class:
public sealed class HttpClientSingleton
{
private static readonly Lazy<HttpClient> lazy = new Lazy<HttpClient>(() => new HttpClient());
public static HttpClient Instance { get { return lazy.Value; } }
private HttpClientSingleton()
{
}
}
So putting it together. Here is an example of getting some loginfo from the API.
public class MyLogController : BaseController
{
[HttpPost]
public async Task<JsonResult> log(string requestId)
{
var url = ApiBaseAdress + string.Format("/api/runs/log/{0}", requestId);
List<Log> logs = new List<Log>();
var response = await HttpClientSingleton.Instance.GetAsync(url);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
logs = JsonConvert.DeserializeObject<List<Log>>(result);
return Json(logs);
}
}
You can write a static helper class. If the name is ApiHelper, then add a Microsoft.AspNet.WebApi.Client reference. When your app is initialized, call the class's InitializeClient() method, and you can call the GetAsync() method if you need. The code is below:
public static class ApiHelper
{
public static HttpClient ApiClient { get; set; }
public static void InitializeClient()
{
ApiClient = new HttpClient();
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public static async Task<T> GetAsync<T>(string url)
{
using (HttpResponseMessage response = await ApiHelper.ApiClient.GetAsync(url))
{
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<T>();
return result;
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
}
I'm trying to get response from soundcloud API. Here is my code.
public static async Task<string> GetTheGoodStuff()
{
var client = new HttpClient(new NativeMessageHandler());
var response = await client.GetAsync("http://api.soundcloud.com/playlists?client_id=17ecae4040e171a5cf25dd0f1ee47f7e&limit=1");
var responseString = response.Content.ReadAsStringAsync().Result;
return responseString;
}
But it's stucks on var response = await client.GetAsync. How can I fix this?
Thanks!
I did just use your code in a PCL, only thing I changed is the url (to https) to satisfy iOS ATS requirements, and called it from an async method. Seems to work fine running on iOS device. I did grab references to Microsoft.Net.Http in the PCL, and ModernHttpClient in the PCL and in the platform-specific projects (via NuGet).
Your code in some PCL view model class:
using System.Net.Http;
using System.Threading.Tasks;
using ModernHttpClient;
public class ItemsViewModel
{
...
public async Task<string> GetPlaylist()
{
// Use https to satisfy iOS ATS requirements.
var client = new HttpClient(new NativeMessageHandler());
var response = await client.GetAsync("https://api.soundcloud.com/playlists?client_id=17ecae4040e171a5cf25dd0f1ee47f7e&limit=1");
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
...
}
Then in a PCL page class that instantiates and uses an instance of the view model:
public partial class ItemsPage : ContentPage
{
public ItemsPage()
{
InitializeComponent();
Vm = new ItemsViewModel();
BindingContext = Vm;
}
protected override async void OnAppearing()
{
var playlist = await Vm.GetPlaylist();
// Do something cool with the string, maybe some data binding.
}
// Public for data binding.
public ItemsViewModel Vm { get; private set; }
}
Hope this helps.
I have the same problem. I fixed it by:
var response = httpClient.GetAsync(ApiUrl).ConfigureAwait(false).GetAwaiter().GetResult();
you can try it.
I'm working on a project that retrieves information from an external webservice API, but I'm not sure how I'm supposed to test it, I'm quite new at Testing and I have done just a couple of Unit Test, but as far as I know I have to mock the webservice functionality, I've been looking for info regarding this subject but haven't found anything for Windows Phone yet. What's the standard procedure for these type of cases?
Here's a simple version of what I want to test:
public async Task<List<Song>> FetchSongsAsync(String query)
{
if (String.IsNullOrEmpty(query))
return null;
string requestUrl = "webservice url";
var client = new HttpClient();
var result = await client.GetStringAsync(new Uri(requestUrl,UriKind.Absolute));
try
{
var result = JsonConvert.DeserializeObject<RootObject>(result);
return result;
}
catch (Exception)
{
return null;
}
}
Thanks!
Decouple your code from its dependencies: make content loading and its deserialization replaceable:
private readonly IClient client;
private readonly ISerializer serializer;
public YourService(IClient client, ISerializer serializer)
{
_client = client;
_serializer = serializer;
}
public async Task<List<Song>> FetchSongsAsync(String query)
{
try
{
var result = await _client.GetStringAsync(new Uri("http://example.com"));
return _serializer.DeserializeObject<RootObject>(result);
}
catch (Exception)
{
return null;
}
}
The first thing that may help is understand and use dependency injection. Basically taking any dependencies of your object/method/etc and (as it states) injecting them into the object/method/etc. For example, you are having a difficult time figuring out how to test the method because the method depends on being able to access the web service. There are a couple things you can do after this.
One thing to do is to check out mocking frameworks such as Moq.
Another thing I recently did was I added an overloaded constructor (dependency injection) that takes a HttpMessageInvoker object (note HttpClient derives from this). This way I could instantiate the class with my own response message:
public class MyLoader()
{
protected HttpMessageInvoker MessageInvoker { get; set; }
private HttpRequestMessage requestMessage;
public MyLoader() // default constructor
{
MessageInvoker = new HttpClient();
}
public MyLoader(HttpMessageInvoker httpMessageInvoker)
{
MessageInvoker = httpMessageInvoker;
}
public object DoSomething()
{
var response = await MessageInvoker.SendAsync(requestMessage, cancellationTokenSource.Token);
}
Here is my mock message invoker:
public class MockMessageInvoker : HttpMessageInvoker
{
public string ResponseString { get; set; }
public MockMessageInvoker(string responseString)
: base(new HttpClientHandler())
{
ResponseString = responseString;
}
public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
return Task.Run<HttpResponseMessage>(() =>
{
HttpResponseMessage responseMessage = new HttpResponseMessage(
System.Net.HttpStatusCode.OK);
var bytes = Encoding.ASCII.GetBytes(ResponseString);
var stream = new System.IO.MemoryStream(bytes);
responseMessage.Content = new StreamContent(stream);
return responseMessage;
});
}
}
I can call it all like so:
MyLoader loader = new MyLoader(new MockMessageInvoker(validJsonResponse));
loader.DoSomething() // I've removed the dependency on the service and have control of the content in the response
It's quick and dirty, but does the trick.
Hope this helps.