I have two classes implementing the interfaces. Both the classes execute place search functionalities. Also in both the classe constructor there is a common functionality depending on the classes
Google : IGoogle
public NearBySearch(IWebPortalApiClient webPortalApiClient)
{
var serverString = ConfigHelper.GetAppSetting(ConfigConstants.APIServerType);
var server = (WebPortalServer)Enum.Parse(typeof(WebPortalServer), serverString, true);
this._webPortalApiClient = webPortalApiClient;
this.GoogleKey = $"&key={ConfigHelper.GetAppSetting(ConfigConstants.Googlekey)}";
this._webPortalApiClient.Init(server);
}
Yelp : IYelp
public BusinessSearch(IWebPortalApiClient webPortalApiClient)
{
var serverString = ConfigHelper.GetAppSetting(ConfigConstants.APIServerType);
var server = (WebPortalServer)Enum.Parse(typeof(WebPortalServer), serverString, true);
this._webPortalApiClient = webPortalApiClient;
this._webPortalApiClient.AccessToken = ConfigHelper.GetAppSetting(ConfigConstants.YelpApiKey);
this._webPortalApiClient.Init(server, this._webPortalApiClient.AccessToken);
}
In case of google we have to send key as a query parameter where as in Yelp we have to send key as Authorization header
In API Controller I am injecting Both
IGoogle
private IYelpController _yelpController;
private IGoogleController _googleController;
public PlaceSearchController(IYelpController yelpController, IGoogleController googleController)
{
this._yelpController = yelpController;
this._googleController = googleController;
}
The function that we are callin in API is like
[HttpGet]
public async Task<IHttpActionResult> GetAllBusiness(int web, decimal latitude, decimal longitude, string radius = null)
{
try
{
if (web == (int)PlaceSearch.Yelp)
{
var result = await _yelpController.GetAllBusiness(latitude, longitude, radius);
var response = new Response<Common.Models.Yelp.Yelp>(ResponseConstants.Success, ResponseConstants.SuccessMessage, result);
return Ok(response);
}
else if(web == (int)PlaceSearch.Google)
{
if(radius == null)
{
throw new Exception();
}
var result = await _googleController.GetAllNearByPlaces(latitude, longitude, radius);
var response = new Response<Common.Models.Google.Google>(ResponseConstants.Success, ResponseConstants.SuccessMessage, result);
return Ok(response);
}
throw new Exception();
}
catch (Exception ex)
{
var response = new Response<Object>(ResponseConstants.Forbidden, ResponseConstants.FailureMessage, null);
return Ok(response);
}
}
The issue I am facing is below function is called in both the constructor
public IWebPortalApiClient Init(WebPortalServer server, string accessToken = null)
{
WebPortalServer = server;
AccessToken = accessToken;
return this;
}
When Yelp is executed asses token is passed which is as expected but at the same time we dont pass access token in Google which sets the access token to null.
Because of this issue I am not able to call Yelp API as it does not find the access token.
Is there a way that we inject dependency on conditional basis. We are using Unity Container for this.
Web Portal Client
public class WebPortalApiClient : IWebPortalApiClient
{
public WebPortalServer WebPortalServer { get; set; }
public string AccessToken { get; set; }
private string ServerUrl
{
get
{
switch (WebPortalServer)
{
case WebPortalServer.Dev:
return null;
case WebPortalServer.QA:
return null;
case WebPortalServer.Demo:
return null;
case WebPortalServer.Production:
return null;
case WebPortalServer.Localhost:
return "http://localhost:63695/";
case WebPortalServer.Integration:
return null;
case WebPortalServer.UAT:
return null;
default:
throw new ArgumentOutOfRangeException();
}
}
}
public async Task<HttpContent> InvokeApi(string path, HttpAction action, HttpContent content = null, TimeSpan? overrideTimeout = null, string externalServer = null)
{
var sUrl = externalServer == null ? ServerUrl : externalServer;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(sUrl);
if (overrideTimeout.HasValue)
{
client.Timeout = overrideTimeout.Value;
}
//this.Log("Connecting to {0} Api at {1}".Fmt(WebPortalServer, ServerUrl));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", AccessToken);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response;
switch (action)
{
case HttpAction.Get:
response = await client.GetAsync(path);
break;
case HttpAction.Post:
response = await client.PostAsync(path, content);
break;
case HttpAction.Put:
response = await client.PutAsync(path, content);
break;
case HttpAction.Delete:
response = await client.DeleteAsync(path);
break;
default:
throw new ArgumentOutOfRangeException("action", action, null);
}
return response.IsSuccessStatusCode ? response.Content : null;
}
}
public IWebPortalApiClient Init(WebPortalServer server, string accessToken = null)
{
WebPortalServer = server;
AccessToken = accessToken;
return this;
}
}
Related
I have an Api hosted on Azure which I consume on my Xamarin Forms project.
I show the login page at the beginning and I check if the JWT token has expired but I also want to check that on each http method in case it expires while the user is using the app.
So I need to either show the user the login page and tell them to login again I have been searching how to do that I can't get it right.
Here is my AzureApiService class.
public class AzureApiService
{
HttpClient httpClient;
public AzureApiService()
{
#if DEBUG
var httpHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (o, cert, chain, errors) => true
};
#else
var httpHandler = new HttpClientHandler();
#endif
httpClient = new HttpClient(httpHandler);
httpClient.Timeout = TimeSpan.FromSeconds(15);
httpClient.MaxResponseContentBufferSize = 256000;
}
public async Task<string> LoginAsync(string url, AuthUser data)
{
var user = await HttpLoginPostAsync(url, data);
if (user != null)
{
//Save data on constants
CurrentPropertiesService.SaveUser(user);
return user.Token;
}
else
{
return string.Empty;
}
}
// Generic Get Method
public async Task<T> HttpGetAsync<T>(string url, string token)
{
T result = default(T);
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = httpClient.GetAsync(url).Result;
HttpContent content = response.Content;
if (response.IsSuccessStatusCode)
{
var jsonResponse = await content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
}
else
{
if (IsExpired(token))
{
await Logout();
}
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
}
catch (Exception ex)
{
OnError(ex.ToString());
}
return result;
}
// Generic Post Method
public async Task<T> HttpPostAsync<T>(string url, string token, T data)
{
T result = default(T); // résultat de type générique
try
{
string json = JsonConvert.SerializeObject(data);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await httpClient.PostAsync(new Uri(url), content);
var jsonResponse = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var jsons = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
}
else
{
if (IsExpired(token))
{
await Logout();
}
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
return result;
}
catch (Exception ex)
{
OnError(ex.ToString());
return result;
}
}
// Generic Put Method
public async Task<T> HttpPutAsync<T>(string url, string token, T data)
{
T result = default(T); // résultat de type générique
try
{
string json = JsonConvert.SerializeObject(data);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await httpClient.PutAsync(new Uri(url), content);
if (response.IsSuccessStatusCode)
{
var jsonResponse = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
}
else
{
if (IsExpired(token))
{
await Logout();
}
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
return result;
}
catch (Exception ex)
{
OnError(ex.ToString());
return result;
}
}
// Generic Delete Method
public async Task<bool> HttpDeleteAsync(string url, string token)
{
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await httpClient.DeleteAsync(url);
if (response.IsSuccessStatusCode)
{
return true;
}
else
{
if (IsExpired(token))
{
await Logout();
}
return false;
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
}
catch (Exception ex)
{
OnError(ex.ToString());
return false;
}
}
// Login Post Method
public async Task<T> HttpLoginPostAsync<T>(string url, T data)
{
T result = default(T); // résultat de type générique
try
{
string json = JsonConvert.SerializeObject(data);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(new Uri(url), content);
if (response.IsSuccessStatusCode)
{
var jsonResponse = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
}
else
{
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
return result;
}
catch (Exception ex)
{
OnError(ex.ToString());
return result;
}
}
public bool IsExpired(string token)
{
if (token == null || "".Equals(token))
{
return true;
}
/***
* Make string valid for FromBase64String
* FromBase64String cannot accept '.' characters and only accepts stringth whose length is a multitude of 4
* If the string doesn't have the correct length trailing padding '=' characters should be added.
*/
int indexOfFirstPoint = token.IndexOf('.') + 1;
String toDecode = token.Substring(indexOfFirstPoint, token.LastIndexOf('.') - indexOfFirstPoint);
while (toDecode.Length % 4 != 0)
{
toDecode += '=';
}
//Decode the string
string decodedString = Encoding.ASCII.GetString(Convert.FromBase64String(toDecode));
//Get the "exp" part of the string
Regex regex = new Regex("(\"exp\":)([0-9]{1,})");
Match match = regex.Match(decodedString);
long timestamp = Convert.ToInt64(match.Groups[2].Value);
DateTime date = new DateTime(1970, 1, 1).AddSeconds(timestamp);
DateTime compareTo = DateTime.UtcNow;
int result = DateTime.Compare(date, compareTo);
return result < 0;
}
private async Task Logout()
{
CurrentPropertiesService.Logout();
CurrentPropertiesService.RemoveCart();
await Shell.Current.GoToAsync($"//main");
}
private void OnError(string error)
{
Console.WriteLine("[WEBSERVICE ERROR] " + error);
}
}
So you can see that in each http method I'm trying yo check if the token has expired already and then logout but it just gives an error.
On my Logout method I just want to delete all the properties and then navigate to the login page but it isn't working.
Please help I would like to know how to do this. Thanks.
EDIT
Trying to implement DelegatingHandler stops at SendAsync
Here is my HttpDelegatingHandler class
public class HttpDelegatingHandler : DelegatingHandler
{
public HttpDelegatingHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("Bearer", CurrentPropertiesService.GetToken());
// before request
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
// after request
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
await Logout();
}
return response;
}
private async Task Logout()
{
CurrentPropertiesService.Logout();
CurrentPropertiesService.RemoveCart();
await Shell.Current.GoToAsync($"//main");
}
}
Here my AzureApiService class
public class AzureApiService
{
HttpClient httpClient;
public AzureApiService()
{
var clientHandler = new HttpClientHandler();
#if DEBUG
clientHandler.ServerCertificateCustomValidationCallback =
(sender, cert, chain, sslPolicyErrors) =>
{
return true;
};
#endif
httpClient = new HttpClient(new HttpDelegatingHandler(clientHandler));
httpClient.Timeout = TimeSpan.FromSeconds(15);
httpClient.MaxResponseContentBufferSize = 256000;
}
public async Task<string> LoginAsync(string url, AuthUser data)
{
var user = await HttpLoginPostAsync(url, data);
if (user != null)
{
//Save data on constants
CurrentPropertiesService.SaveUser(user);
return user.Token;
}
else
{
return string.Empty;
}
}
// Generic Get Method
public async Task<T> HttpGetAsync<T>(string url, string token)
{
T result = default(T);
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await httpClient.GetAsync(url);
HttpContent content = response.Content;
var jsonResponse = await content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
catch (Exception ex)
{
OnError(ex.ToString());
}
return result;
}
It works for PostAsync
// Login Post Method
public async Task<T> HttpLoginPostAsync<T>(string url, T data)
{
T result = default(T); // résultat de type générique
try
{
string json = JsonConvert.SerializeObject(data);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(new Uri(url), content);
var jsonResponse = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
return result;
}
catch (Exception ex)
{
OnError(ex.ToString());
return result;
}
}
But as I said it stops when trying to get data
You can handle 401 Unauthorized response in a custom Delegating handler. This way you can handle anything before and after request execution in a single place.
public class HttpDelegatingHandler : DelegatingHandler
{
public HttpDelegatingHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("Authorization", string.Format("Basic {0}", MyUserRepository.AuthToken));
// before request
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
// after request
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
await Shell.Current.GoToAsync($"//main");
}
return response;
}
}
public class AzureApiService
{
HttpClient httpClient;
public AzureApiService()
{
var clientHandler = new HttpClientHandler();
#if DEBUG
clientHandler.ServerCertificateCustomValidationCallback =
(sender, cert, chain, sslPolicyErrors) =>
{
return true;
};
#endif
httpClient = new HttpClient(new HttpDelegatingHandler(clientHandler));
httpClient.Timeout = TimeSpan.FromSeconds(15);
httpClient.MaxResponseContentBufferSize = 256000;
}
....
// Generic Get Method
public async Task<T> HttpGetAsync<T>(string url, string token)
{
T result = default(T);
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = await httpClient.GetAsync(url);
var jsonResponse = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
}
catch (Exception ex)
{
OnError(ex.ToString());
}
return result;
}
Basically I get
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object occurred
when I try to get the token from the restservice.
when it gets to
public async Task<T> PostResponseLogin<T>(string weburl, FormUrlEncodedContent content) where T : class
{
var response = await client.PostAsync(weburl, content);
var jsonResult = response.Content.ReadAsStringAsync().Result;
var responseObject = JsonConvert.DeserializeObject<T>(jsonResult);
return responseObject;
}
on the return respondObject; it goes to the appDelegate and throws the exception.
-I'm currently in the learning process of C#/Xamarin, so if I have made a simple mistake that you notice, that will be why. Thanks for your help.
I've added MainPage = new NavigationPage(new LoginPage()); so I can navigate between pages, that is working.
edit: It was suggested that this might be a possible duplicate of another ticket with an unhandled exception. While it did help me to understand that error better, it's still more specific than that.
public class RestService
{
HttpClient client;
string grant_type = "password";
public RestService()
{
client = new HttpClient();
client.MaxResponseContentBufferSize = 256000;
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded' "));
}
public async Task<Token> Login(User user)
{
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("grant_type", grant_type));
postData.Add(new KeyValuePair<string, string>("Email", user.Email));
postData.Add(new KeyValuePair<string, string>("Password", user.Password));
var content = new FormUrlEncodedContent(postData);
var weburl = "https://blahblah.com/auth/login";
var response = await PostResponseLogin<Token>(weburl, content);
DateTime dt = new DateTime();
dt = DateTime.Today;
response.expireDate = dt.AddSeconds(response.expireIn);
return response;
}
public async Task<T> PostResponseLogin<T>(string weburl, FormUrlEncodedContent content) where T : class
{
var response = await client.PostAsync(weburl, content);
var jsonResult = response.Content.ReadAsStringAsync().Result;
var responseObject = JsonConvert.DeserializeObject<T>(jsonResult);
return responseObject;
}
public async Task<T> PostResponse<T>(string weburl, string jsonstring) where T : class
{
var Token = App.TokenDatabase.GetToken();
string ContentType = "application/json";
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Token.accessToken);
try
{
var Result = await client.PostAsync(weburl, new StringContent(jsonstring, Encoding.UTF8, ContentType));
if (Result.StatusCode == System.Net.HttpStatusCode.OK)
{
var JsonResult = Result.Content.ReadAsStringAsync().Result;
try
{
var ContentResp = JsonConvert.DeserializeObject<T>(JsonResult);
return ContentResp;
}
catch { return null; }
}
}
catch { return null; }
return null;
}
public async Task<T> GetResponse<T>(string weburl) where T : class
{
var Token = App.TokenDatabase.GetToken();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Token.accessToken);
try
{
var response = await client.GetAsync(weburl);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var JsonResult = response.Content.ReadAsStringAsync().Result;
try
{
var ContentResp = JsonConvert.DeserializeObject<T>(JsonResult);
return ContentResp;
}
catch
{
return null;
}
}
}
catch
{
return null;
}
return null;
}
}
I have a Web Api project.
This method calling the web api.
public User GetUser(Expression<Func<User, bool>> where)
{
HttpClient Client = new HttpClient();
Client.BaseAddress = new Uri("http://192.168.2.139:23283/api/user/");
var responseTask = Client.GetAsync("GetUserByExpression?id=" + where);
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<User>();
readTask.Wait();
return readTask.Result;
}
else
{
return new User();
}
}
var test=GetUser(p=>p.Id==1);
After using the method, it enters the api but the parameter is null.
[HttpGet]
[Route("GetUserByExpression")]
public HttpResponseMessage GetUserByExpression(Expression<Func<User, bool>> where)
{
try
{
var user = repoUser.Find(where);
if (user != null)
{
return Request.CreateResponse(HttpStatusCode.OK, user);
}
else
{
return Request.CreateResponse(HttpStatusCode.NotFound, "User not found.");
}
}
catch (Exception exp)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, exp.Message);
}
}
The "where" parameter is null when I call the Get method. Therefore, I can not make a healthy operation. What can I do to prevent this parameter from going to null?
I am trying to register an iOS device to Azure Notification Hubs
Registration works - no error is thrown.
calling await _hub.GetAllRegistrationsAsync(0) gets 0 records
sending a test message returns Message was successfully sent, but there were no matching targets.
Implementation:
I have a AzurePushNotificationsService class, which i use to register the device.
public class AzurePushNotificationsService
{
private readonly NotificationHubClient _hub;
public AzurePushNotificationsService()
{
_hub = NotificationHubClient.CreateClientFromConnectionString("Endpoint=[endpoint]/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=[access_key]", "push_app_dev");
}
private async Task<string> RegisterDevice(string token)
{
string newRegistrationId = null;
if (!string.IsNullOrWhiteSpace(token))
{
var registrations = await _hub.GetRegistrationsByChannelAsync(token, 100);
foreach (var registration in registrations)
{
if (newRegistrationId == null)
{
newRegistrationId = registration.RegistrationId;
}
else
{
await _hub.DeleteRegistrationAsync(registration);
}
}
}
return newRegistrationId ?? await _hub.CreateRegistrationIdAsync();
}
public async Task<string> CreateOrUpdateRegistration(string token, DeviceType deviceType, string registrationId = null)
{
if (string.IsNullOrWhiteSpace(registrationId))
registrationId = await RegisterDevice(token);
RegistrationDescription registration = null;
switch (deviceType)
{
case DeviceType.Apple:
registration = new AppleRegistrationDescription(token);
break;
}
registration.RegistrationId = registrationId;
var registrationStale = false;
try
{
// throws no error.
registration = await _hub.CreateOrUpdateRegistrationAsync(registration);
}
catch (MessagingException e)
{
var webEx = e.InnerException as WebException;
if (webEx != null && webEx.Status == WebExceptionStatus.ProtocolError)
{
var response = (HttpWebResponse)webEx.Response;
if (response.StatusCode == HttpStatusCode.Gone)
{
registrationStale = true;
}
}
}
// if the registration is stale and/or removed then it needs to be re-created with a new registrationId
if (registrationStale)
registrationId = await CreateOrUpdateRegistration(contactId, token, deviceType);
return registrationId;
}
}
I register a device:
string token = "9da952de6877b23738f5e744b6149750505417afc84d31290044643fa1493d2c".ToUpper();
var service = new AzurePushNotificationsService();
await service.CreateOrUpdateRegistration(token, DeviceType.Apple, null);
I send a Test message, and i get the message:
Message was successfully sent, but there were no matching targets.
Is it something that i am missing?
I am using Moq in .net core(1.1) and having a bit of torrid time understanding this behavior as all the examples on interweb points to the fact the this should work with no issues.
I have already tried with:
Returns(Task.FromResult(...)
Returns(Task.FromResult(...)
ReturnsAsync(...)
Mocking a IHttpClient interface to wrap PostAsync, PutAsync and GetAsync. All of these return an ApiResponse object.
var mockClient = new Mock<IHttpClient>();
Does not work:
mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null))
.Returns(Task.FromResult(new ApiResponse()));
PostSync definition:
public async Task<ApiResponse> PostAsync(string url, string body, string authToken = null)
Does work:
mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null))
.Returns(Task.FromResult(bool));
PostSync definition:
public async Task<bool> PostAsync(string url, string body, string authToken = null)
Usage:
var api = new ApiService(mockClient.Object);
var response = api.LoginAsync(body.Username, body.Password);
UPDATE
[Fact]
public async void TestLogin()
{
var mockClient = new Mock<IHttpClient>();
mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null)).Returns(Task.FromResult(new ApiResponse()));
var api = new ApiService(mockClient.Object);
var response = await api.LoginAsync(body.Username, body.Password);
Assert.IsTrue(response);
}
Return Type:
public class ApiResponse
{
public string Content { get; set; }
public HttpStatusCode StatusCode { get; set; }
public string Reason { get; set; }
}
LoginAsync:
public async Task<bool> LoginAsync(string user, string password)
{
var body = new { Username = user, Password = password };
try
{
var response = await _http.PostAsync(_login_url, JsonConvert.SerializeObject(body), null);
return response .State == 1;
}
catch (Exception ex)
{
Logger.Error(ex);
return false;
}
}
PostAsync:
public async Task<object> PostAsync(string url, string body, string authToken = null)
{
var client = new HttpClient();
var content = new StringContent(body, Encoding.UTF8, "application/json");
var response = await client.PostAsync(new Uri(url), content);
var resp = await response.Result.Content.ReadAsStringAsync();
return new ApiResponse
{
Content = resp,
StatusCode = response.Result.StatusCode,
Reason = response.Result.ReasonPhrase
};
}
Assuming a simple method under test like this based on minimal example provided above.
public class ApiService {
private IHttpClient _http;
private string _login_url;
public ApiService(IHttpClient httpClient) {
this._http = httpClient;
}
public async Task<bool> LoginAsync(string user, string password) {
var body = new { Username = user, Password = password };
try {
var response = await _http.PostAsync(_login_url, JsonConvert.SerializeObject(body), null);
return response.StatusCode == HttpStatusCode.OK;
} catch (Exception ex) {
//Logger.Error(ex);
return false;
}
}
}
The following test works when configured correctly
[Fact]
public async Task Login_Should_Return_True() { //<-- note the Task and not void
//Arrange
var mockClient = new Mock<IHttpClient>();
mockClient
.Setup(x => x.PostAsync(It.IsAny<string>(), It.IsAny<string>(), null))
.ReturnsAsync(new ApiResponse() { StatusCode = HttpStatusCode.OK });
var api = new ApiService(mockClient.Object);
//Act
var response = await api.LoginAsync("", "");
//Assert
Assert.IsTrue(response);
}
The above is just for demonstrative purposes only to show that it can work provided the test is configured properly and exercised based on the expected behavior.
Take some time and review the Moq quick start to get a better understanding of how to use the framework.