Refactor code to make more generic method - c#

I want to try and refactor this method a bit better while I am having some difficulty in understand the async I have a web api2 project which I use my data access layer to share to a phone app. I am not sure if I have the syntax correct I am using a xamrian shared library here in a xamrian forms app.
I will have various methods link get clients which will have the end point API/clients but obv their return type would be different.
How would one make the below work well with a list view say.
How would I consume the below method as well , what is general practise to then store the jobs locally in sql lite.
public string BaseUrl { get; set; }
public string EndPoint { get; set; }
Lets go out to the web service and grab the job list.
public async List<Job> GetJSON()
{
List<Job> rootObject = new List<Job>();
try
{
var client = new System.Net.Http.HttpClient();
var response = await client.GetAsync("http://myinternaliis/api/job");
string json = await response.Content.ReadAsStringAsync();
if (json != "")
{
rootObject = JsonConvert.DeserializeObject< List<Job>>(json);
}
}
catch (InvalidCastException e)
{
throw e;
}
return await rootObject;
}
Thanks for help in improving my understanding.

I guess you are looking for something like:
public async Task<T> GetJson<T>(string url)
{
using (var client = new System.Net.Http.HttpClient())
{
var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
return (T)JsonConvert.DeserializeObject<T>(json);
}
}
Usually I have:
IApi - defines all the API methods
IHttpService - defines methods like Get, Post and etc.
IJsonConverter - defines methods like serialize and deserialize.
Here is an example:
public interface IJsonConverter
{
T Deserialize<T>(string json);
string Serialize<T>(T data);
}
public class JsonConveter : IJsonConverter
{
public T Deserialize<T>(string json) => JsonConvert.DeserializeObject<T>(json);
public string Serialize<T>(T data) => JsonConvert.Serialize(data);
}
public interface IHttpService
{
Task<T> Get<T>(string url);
}
public class HttpService : IHttpService
{
readonly IJsonConverter jsonConverter;
public HttpService(IJsonConverter jsonConverter)
{
this.jsonConverter = jsonConverter;
}
public async Task<T> Get<T>(string url)
{
using (var client = new System.Net.Http.HttpClient())
{
var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
return jsonConverter.Deserialize<T>(json);
}
}
}
public interface IApi
{
Task<List<Job>> GetJobs();
}
public class Api : IApi
{
readonly string url = "http://myinternaliis/api/";
readonly IHttpService httpService;
public Api(IHttpService httpService)
{
this.httpService = httpService;
}
public Task<List<Job>> GetJobs() => httpService.Get<List<Job>>($"{url}job");
}

Related

Refactoring code to allow for unit testing of HttpClient

I am dealing with a piece of code that looks like this:
public class Uploader : IUploader
{
public Uploader()
{
// assign member variables to dependency injected interface implementations
}
public async Task<string> Upload(string url, string data)
{
HttpResponseMessage result;
try
{
var handler = new HttpClientHandler();
var client = new HttpClient(handler);
result = await client.PostAsync(url, new FormUrlEncodedContent(data));
if (result.StatusCode != HttpStatusCode.OK)
{
return "Some Error Message";
}
else
{
return null; // Success!
}
}
catch (Exception ex)
{
// do some fancy stuff here
}
}
}
I am trying to unit test the Upload function. In particular, I need to mock the HttpClient. After reading the other answers on here and these two articles, I know that one of the better ways to solve this is to mock the HttpMessageHandler instead and pass that to HttpClient and have it return whatever I want.
So, I started along that path by first passing in HttpClient in the constructor as a dependency:
public class Uploader : IUploader
{
private readonly HttpClient m_httpClient; // made this a member variable
public Uploader(HttpClient httpClient) // dependency inject this
{
m_httpClient = httpClient;
}
public async Task<string> Upload(string url, string data)
{
HttpResponseMessage result;
try
{
var handler = new HttpClientHandler();
result = await m_httpClient.PostAsync(url, new FormUrlEncodedContent(data));
if (result.StatusCode != HttpStatusCode.OK)
{
return "Some Error Message";
}
else
{
return null; // Success!
}
}
catch (Exception ex)
{
// do some fancy stuff here
}
}
}
and adding: services.AddSingleton<HttpClient>(); to the ConfigureServices method of Startup.cs.
But now I face a slight issue where the original code specifically creates a HttpClientHandler to pass in. How then do I refactor that to take in a mockable handler?
I find the simplest way is to continue using HttpClient, but pass in a mocking HttpClientHandler such as https://github.com/richardszalay/mockhttp
Code sample from the link above:
var mockHttp = new MockHttpMessageHandler();
mockHttp.When("http://localhost/api/user/*")
.Respond("application/json", "{'name' : 'Test McGee'}");
// Inject the handler or client into your application code
var client = mockHttp.ToHttpClient();
var response = await client.GetAsync("http://localhost/api/user/1234");
var json = await response.Content.ReadAsStringAsync();
Console.Write(json); // {'name' : 'Test McGee'}
The Dependency Injection framework built into .NET Core ignores internal constructors, so it will call the parameter-less constructor in this scenario.
public sealed class Uploader : IUploader
{
private readonly HttpClient m_httpClient;
public Uploader() : this(new HttpClientHandler())
{
}
internal Uploader(HttpClientHandler handler)
{
m_httpClient = new HttpClient(handler);
}
// regular methods
}
In your unit tests, you can use the constructor accepting the HttpClientHandler:
[Fact]
public async Task ShouldDoSomethingAsync()
{
var mockHttp = new MockHttpMessageHandler();
mockHttp.When("http://myserver.com/upload")
.Respond("application/json", "{'status' : 'Success'}");
var uploader = new Uploader(mockHttp);
var result = await uploader.UploadAsync();
Assert.Equal("Success", result.Status);
}
Normally I'm not a big fan of having an internal constructor to facilitate testing, however, I find this more obvious and self-contained than registering a shared HttpClient.
HttpClientFactory might be another good option, but I haven't played around with that too much, so I'll just give info on what I've found useful myself.
One way would be to abstract your HTTP functionality into a service i.e. HttpService which implements an interface of IHttpService:
IHttpService
public interface IHttpService
{
Task<HttpResponseMessage> Post(Uri url, string payload, Dictionary<string, string> headers = null);
}
HttpService
public class HttpService : IHttpService
{
private static HttpClient _httpClient;
private const string MimeTypeApplicationJson = "application/json";
public HttpService()
{
_httpClient = new HttpClient();
}
private static async Task<HttpResponseMessage> HttpSendAsync(HttpMethod method, Uri url, string payload,
Dictionary<string, string> headers = null)
{
var request = new HttpRequestMessage(method, url);
request.Headers.Add("Accept", MimeTypeApplicationJson);
if (headers != null)
{
foreach (var header in headers)
{
request.Headers.Add(header.Key, header.Value);
}
}
if (!string.IsNullOrWhiteSpace(payload))
request.Content = new StringContent(payload, Encoding.UTF8, MimeTypeApplicationJson);
return await _httpClient.SendAsync(request);
}
public async Task<HttpResponseMessage> Post(Uri url, string payload, Dictionary<string, string> headers = null)
{
return await HttpSendAsync(HttpMethod.Post, url, payload, headers);
}
}
Add to your services:
services.AddSingleton<IHttpService, HttpService>();
In your class you would then inject IHttpService as a dependency:
public class Uploader : IUploader
{
private readonly IHttpService _httpService; // made this a member variable
public Uploader(IHttpService httpService) // dependency inject this
{
_httpService = httpService;
}
public async Task<string> Upload(string url, string data)
{
HttpResponseMessage result;
try
{
result = await _httpService.PostAsync(new Uri(url), data);
if (result.StatusCode != HttpStatusCode.OK)
{
return "Some Error Message";
}
else
{
return null; // Success!
}
}
catch (Exception ex)
{
// do some fancy stuff here
}
}
}
You could then use Moq to mock HttpService in your unit test:
[TestClass]
public class UploaderTests
{
private Mock<IHttpService> _mockHttpService = new Mock<IHttpService>();
[TestMethod]
public async Task WhenStatusCodeIsNot200Ok_ThenErrorMessageReturned()
{
// arrange
var uploader = new Uploader(_mockHttpService.Object);
var url = "someurl.co.uk";
var data = "data";
// need to setup your mock to return the response you want to test
_mockHttpService
.Setup(s => s.PostAsync(url, data))
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError));
// act
var result = await uploader.Upload(new Uri(url), data);
// assert
Assert.AreEqual("Some Error Message", result);
}
[TestMethod]
public async Task WhenStatusCodeIs200Ok_ThenNullReturned()
{
// arrange
var uploader = new Uploader(_mockHttpService.Object);
var url = "someurl.co.uk";
var data = "data";
// need to setup your mock to return the response you want to test
_mockHttpService
.Setup(s => s.PostAsync(new Uri(url), data))
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK));
// act
var result = await uploader.Upload(url, data);
// assert
Assert.AreEqual(null, result);
}
}

Passing generic functions with paramaters

I think I might be retarded or I'm asking too much out of C# but I can't get this to work.
What essentially I'm trying to do is to wrap an API-client with some logging functions and a method to request a new token from the API-server.
controller:
public class TestController : ApiController
{
[HttpGet]
[Route("api/model/get/{id}"]
public IHttpActionResult GetModel<Model>(int id)
{
var result = Service.DoHttp<Model>(ServiceClass.GetModel, id);
}
}
service:
public static class ServiceClass
{
private static readonly HttpClient client = new HttpClient() { BaseAddress = new Uri(Globals.ExternalApiPath) };
private static string TokenHeader = "";
public async static Task<HttpResponseMessage> GetModel(int id)
{
var response = client.GetAsync($"/api/get/{id}");
return await response;
}
public static T DoHttp<T>(Func<int, HttpResponseMessage> funk, int id)
{
try
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", TokenHeader);
var result = funk(id);
if (result.IsSuccessStatusCode)
{
return result.Content.ReadAsAsync<T>().Result;
}
else
{
throw new Exception(String.Format($"Unknown error! Unable to contact remote API! AccessToken: {TokenHeader} Status code: {result.StatusCode}"));
}
}
catch (Exception e)
{
// log ex
throw e;
}
}
}
But my Service.DoHttp(Service.GetModel, id); complains about it being the wrong return type.
What am I doing wrong or have I misunderstood the whole concept?
EDIT: Compiler complains about 'Task ServiceClass.GetModel(int)' has the wrong return type
Change the DoHttp method to the following.
public static T DoHttp<T>(Func<int, Task<HttpResponseMessage>> funk, int id)
As the GetModel method returns a Task you need to use a task as the return type of the Func too.

How to get object on ASP.NET Web API?

This is my Web API and it works fine, I mean when i enter this URL on my browser:
http://localhost:18207/api/values/GetMyClass
I retrieve this result:
<MyClass>
<A>a</A>
<b>b</b>
</MyClass>
My codes:
public class MyClass
{
public MyClass()
{
this.A = "a";
this.b = "b";
}
public string A { get; set; }
public string b { get; set; }
}
public class ValuesController : ApiController
{
public MyClass GetMyClass()
{
return new MyClass();
}
}
I have another console application to use my Web API and want to know,
How can i have a complex or object type of MyClass?
Codes on my Console are below but it returns string type
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
MainAsync(args, cts.Token).Wait();
}
static async Task MainAsync(string[] args, CancellationToken token)
{
string baseAddress = "http://localhost:18207/api/values/GetMyClass";
using (var httpClient = new HttpClient())
{
string response = await httpClient.GetStringAsync(baseAddress);
}
}
Your response is probably coming to your console application as JSON (the reason your browser receives it as XML is because of different Accept headers, you can learn about that if you look at Content Negotiation). So what you need to do is parse the JSON and have it deserialize it into your object. There's quite a few libraries that can do that for you.
First make sure that your MyClass is defined in a Class Library project that both your Web API project and your Console project are referencing. This allows us to reuse the class definition without needing to have a separate copy in both projects.
Next, you need a JSON parsing library. There's one built into .NET, but there's a 3rd party one called Json.NET that is the gold standard. My answer will use that one since I'm more familiar with it. Install the Newtonsoft.Json package into your console app.
Then, change your console app as follows:
using Newtonsoft.Json; // at the top of your file
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
MainAsync(args, cts.Token).Wait();
}
static async Task MainAsync(string[] args, CancellationToken token)
{
string baseAddress = "http://localhost:18207/api/values/GetMyClass";
using (var httpClient = new HttpClient())
{
string json = await httpClient.GetStringAsync(baseAddress);
MyClass instance = JsonConvert.DeserializeObject<MyClass>(json);
}
}
The JsonConvert class handles serializing and deserializing the JSON. When deserializing, we just tell is which class to deserialize to and it will attempt to convert the JSON to an instance of that class and return it.
You can use method "GetAsync" which will return object of class "HttpResponseMessage" and then you can call "ReadAsAsync" on Content property.
Please see below code:
public class MyClass
{
public MyClass()
{
this.A = "a";
this.b = "b";
}
public string A { get; set; }
public string b { get; set; }
}
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
MainAsync(args, cts.Token).Wait();
}
static async Task MainAsync(string[] args, CancellationToken token)
{
string baseAddress = "http://localhost:18207/api/values/GetMyClass";
using (var httpClient = new HttpClient())
{
HttpResponseMessage response = await httpClient.GetAsync(baseAddress);
response.EnsureSuccessStatusCode();
MyClass result = await response.Content.ReadAsAsync< MyClass>();
}
}
Here is the full solution end-to-end. We are hosting a Web Api that returns MyClass and then we are calling the API and getting data formatted as XML through a console application.
First, we have MyClass annotated as a DataContract:
[DataContract]
public class MyClass
{
public MyClass()
{
this.A = "a";
this.b = "b";
}
[DataMember]
public string A { get; set; }
[DataMember]
public string b { get; set; }
}
The MyClass Web API:
[AllowAnonymous]
public class MyClassController : ApiController
{
public MyClass Get()
{
return new MyClass();
}
}
and a Console app that uses HttpWebRequest to call the Web Api.
Here's that code (the bottom half is from my original post):
static void Main(string[] args)
{
// this is my Web API Endpoint
var req = (HttpWebRequest)WebRequest.Create("http://localhost:17512/api/MyClass");
// default is JSON, but you can request XML
req.Accept = "application/xml";
req.ContentType = "application/xml";
var resp = req.GetResponse();
var sr = new StreamReader(resp.GetResponseStream());
// read the response stream as Text.
var xml = sr.ReadToEnd();
var ms = new MemoryStream(Encoding.UTF8.GetBytes(xml));
// Deserialize
var ser = new XmlSerializer(typeof(MyClass));
var instance = (MyClass)ser.Deserialize(ms);
Console.WriteLine(instance.A);
Console.WriteLine(instance.b);
var final = Console.ReadLine();
}
NOTE: You'll need to figure out if you want to share a reference to MyClass between the two assemblies or if you just want to have a copy of the code file in each project.
You could just remove XML Formatter inside your WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Removing XML
config.Formatters.Remove(config.Formatters.XmlFormatter);
// Allows us to map routes using [Route()] and [RoutePrefix()]
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Then in your controller you would return just like in your example:
public class ValuesController : ApiController
{
public MyClass GetMyClass()
{
return new MyClass();
}
}
UPDATE 1: I did my answer become more consistent with the question
When making a Request from a Console Application, you could use RestSharp.
var client = new RestClient("http://localhost:18207/");
var request = new RestRequest("api/values/GetMyClass", Method.GET);
var response = client.Execute<MyClass>(request);
if(response.StatusCode == HttpStatusCode.OK)
var responseData = response.Data;
When you execute client.Execute<MyClass>(request) it will deserialize the response into an object of that class. If field names match it should work.

C# async doesn't await

The app I'm working on is supposed to retrieve a json string with the http client after which it gets deserialised and used in the app.
Everything works, except for the await functionality. I'm doing something wrong and I can't seem to figure out what. How can I make sure that my DataService class waits untill I have my json and it has been deserialized?
The DataService class:
class DataService : IDataService
{
private IEnumerable<Concert> _concerts;
public DataService()
{
_concerts = new DataFromAPI()._concerts;
Debug.WriteLine("____Deserialization should be done before continuing____");
**other tasks that need the json**
}
}
My http client class:
class DataFromAPI
{
public IEnumerable<Concert> _concerts { get; set; }
public DataFromAPI()
{
Retrieve();
}
public async Task Retrieve()
{
try
{
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
var result = await client.GetAsync(new Uri("http://url-of-my-api"), HttpCompletionOption.ResponseContentRead);
string jsonstring = await result.Content.ReadAsStringAsync();
DownloadCompleted(jsonstring);
}
catch {}
}
void DownloadCompleted(string response)
{
try
{
_concerts = JsonConvert.DeserializeObject<IEnumerable<Concert>>(response.ToString());
}
catch {}
}
}
solution
After a lot of trial and error I realised that for this particular thingy it didn't have to be async, so I just recreated is on the main thread, with success:
The DataService class:
class DataService : IDataService
{
private IEnumerable<Concert> _concerts;
public DataService()
{
_concerts = new DataFromAPI()._concerts;
}
}
My http client class:
public static class DataFromAPI
{
public void Retrieve()
{
try
{
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
var result = client.GetAsync("http://url-of-my-api").Result;
if (result.IsSuccessStatusCode)
{
var responseContent = result.Content;
}
DownloadCompleted(result.Content.ReadAsStringAsync().Result);
}
catch {}
}
}
You are calling Retrieve() without await in the DataFromAPI constructor, That's why your method isn't awaited.
You should better call this methods outside the constructor, with the await keyword like this :
await Retrieve();
You have to refactor your code a little. Here's an example :
public class DataService : IDataService
{
private IEnumerable<Concert> _concerts;
public async Task LoadData()
{
_concerts = await DataFromAPI.Retrieve();
**other tasks that need the json**
}
}
public static class DataFromAPI
{
public static async Task<IEnumerable<Concert>> Retrieve()
{
try
{
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
var result = await client.GetAsync(new Uri("http://url-of-my-api"), HttpCompletionOption.ResponseContentRead);
string jsonstring = await result.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<Concert>>(response.ToString());
}
catch(Exception)
{
}
return Enumerable.Empty<Concert>();
}
}
Then, when you create your DataService instance, just after you have to call it's LoadData() method.
DataService ds = new DataService();
await ds.LoadData();
And of course, these two lines of code must also be called from an async method. (async / await all the way)
After a lot of trial and error I realised that for this particular thingy it didn't have to be async, so I just recreated is on the main thread, with success:
The DataService class:
class DataService : IDataService
{
private IEnumerable<Concert> _concerts;
public DataService()
{
_concerts = new DataFromAPI()._concerts;
}
}
My http client class:
public static class DataFromAPI
{
public void Retrieve()
{
try
{
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
var result = client.GetAsync("http://url-of-my-api").Result;
if (result.IsSuccessStatusCode)
{
var responseContent = result.Content;
}
DownloadCompleted(result.Content.ReadAsStringAsync().Result);
}
catch {}
}
}

ASP.Net Web API Returning Array Values

Pretty new with ASP.Net WEB API. Having some issues with the proper API configuration (and return type) for my API call which calls another ASHX service.
I have the following codes (tested in HomeController just to verify that the service call would work):
public async Task<ActionResult> Index()
{
WebRequest request = WebRequest.CreateHttp("http://callme/address.ashx");
var response = await request.GetResponseAsync();
string content;
using (var stream = response.GetResponseStream())
using(var reader = new StreamReader(stream))
{
content = await reader.ReadToEndAsync();
}
var result = JsonConvert.DeserializeObject<MyResult[]>(content);
return this.View();
}
public class MyResult
{
public string ClientAddress { get; set; }
}
Now, trying to port it over to an ASP.Net WEB API call:
ClientAddressController.cs
public class ClientAddressController: ApiController
{
public async IQueryable<MyResult> GetClientAddress()
{
WebRequest request = WebRequest.CreateHttp("http://callme/address.ashx");
var response = await request.GetResponseAsync();
string content;
using (var stream = response.GetResponseStream())
using(var reader = new StreamReader(stream))
{
content = await reader.ReadToEndAsync();
}
var result = JsonConvert.DeserializeObject<MyResult[]>(content);
// How to return the result object??
}
}
public class MyResult
{
public string ClientAddress { get; set; }
}
I need some help to properly define the correct parameters for the WEB Api call so that I could return the result object.
The result object would just be an array of strings:
[{"Address": "Address 100"}, {"Address": "Address 200"}, {"Address": "300"}]
Hoping to get some insights on resolving this. I have some idea with regards to returning database queries in Web API, but the service calls (and the async method) kind of threw me off the groove.
Thanks.
**UPDATE*****
Was able to find some resolution on this and I am posting the solution I have.
public class ClientAddressController: ApiController
{
public async Task<IHttpActionResult> GetClientAddress()
{
WebRequest request = WebRequest.CreateHttp("http://callme/address.ashx");
var response = await request.GetResponseAsync();
string content;
using (var stream = response.GetResponseStream())
using(var reader = new StreamReader(stream))
{
content = await reader.ReadToEndAsync();
}
var result = JsonConvert.DeserializeObject<MyResult[]>(content);
return Ok(result);
// How to return the result object??
}
}
public class MyResult
{
public string ClientAddress { get; set; }
}
P.S.: I am going to accept #Stripling's answer as his provided me some direction.
You'll need to create a class with an Address property, and map the results to objects of that class:
public async IQueryable<ClientAddressResult> GetClientAddress()
{
WebRequest request = WebRequest.CreateHttp("http://callme/address.ashx");
var response = await request.GetResponseAsync();
string content;
using (var stream = response.GetResponseStream())
using(var reader = new StreamReader(stream))
{
content = await reader.ReadToEndAsync();
}
IEnumerable<MyResult> result = JsonConvert.DeserializeObject<MyResult[]>(content);
return result.Select(r => new ClientAddressResult{Address = r.ClientAddress})
.AsQueryable();
}
DTO Classes:
public class MyResult
{
public string ClientAddress { get; set; }
}
public class ClientAddressResult
{
public string Address { get; set; }
}
You can return array values as dynamic List to be able to do that set method return with dynamic List.
var resultList = new List<dynamic>();
resultList.Add(new {Address="Address 100"});
resultList.Add(new {Address="Address 200"});
resultList.Add(new {Address="Address 300"});
return resultList;
Hope this is what you are loooking for.

Categories

Resources