I have only one method for Api Request as follow
private async Task<Site> getSiteAsync(string siteId)
{
Site site = null;
var response = await httpClient.SendAsync(
new HttpRequestMessage(HttpMethod.Get, httpClient.BaseAddress + $"api/sites/{siteId}"));
if (response.IsSuccessStatusCode)
{
var stream = await response.Content.ReadAsStreamAsync();
site = await JsonSerializer.DeserializeAsync<Site>(stream, serializerOptions);
}
return site;
}
When I try to call it from my MainClass the first call (a) works fine, but the b,c and d they all return me the Status = WaitingForActivation.
private readonly HttpClient httpClient = new HttpClient();
private readonly JsonSerializerOptions serializerOptions = new JsonSerializerOptions();
public MainWindow()
{
httpClient.BaseAddress = new Uri($"http://localhost:5000/MyApi/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
serializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
var a = getSiteAsync("0001"); << I only get the value of this call.
var b = getSiteAsync("0002");
var c = getSiteAsync("0003");
var d = getSiteAsync("0004");
InitializeComponent();
}
How can I get the Result of b,c and d ?
You should await the calls and since you can only do this in an async method and not in a constructor, you should move your code to a method or an event handler:
public MainWindow()
{
httpClient.BaseAddress = new Uri($"http://localhost:5000/MyApi/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
serializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
Loaded += async (s, e) =>
{
var a = await getSiteAsync("0001");
var b = await getSiteAsync("0002");
var c = await getSiteAsync("0003");
var d = await getSiteAsync("0004");
};
InitializeComponent();
}
Related
I have designed the following method to call api within the loop. I was wondering if there is any other better way (performance) of calling APIs multiple times?
private List<ClientInfo> populateDetails(List<ClientInfo> lstClientDetails)
{
using (var client = new HttpClient())
{
client.BaseAddress = EndpointAddress;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string Uri = "";
ClientInfo clientInfo;
foreach (var record in lstClientDetails)
{
Uri = string.Format("{0}", record.id);
var postResponse = client.GetAsync(Uri).Result;
if (postResponse.IsSuccessStatusCode)
{
string result = postResponse.Content.ReadAsStringAsync().Result;
clientInfo = JsonConvert.DeserializeObject<ClientInfo>(result);
if (clientInfo != null)
record.email = clientInfo.email;
}
}
return lstClientDetails;
}
}
Maybe you can change your code to use async/await pattern
private async Task populateDetails(List<ClientInfo> lstClientDetails)
{
using (var client = new HttpClient())
{
client.BaseAddress = EndpointAddress;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string Uri = "";
ClientInfo clientInfo;
foreach (var record in lstClientDetails)
{
Uri = string.Format("{0}", record.id);
var postResponse = await client.GetAsync(Uri);
if (postResponse.IsSuccessStatusCode)
{
string result = await postResponse.Content.ReadAsStringAsync();
clientInfo = JsonConvert.DeserializeObject<ClientInfo>(result);
if (clientInfo != null)
record.email = clientInfo.email;
}
}
}
}
And as you can see it doesn't make senso to return the same List which is passed as a paramters: the code will change the underlyne objects
Reference Microsoft Docs Parallel Library
sample code:
public class ParallelClient
{
private async Task ParallelRequest(List<string> requests)
{
var responses = new ConcurrentBag<HttpResponseMessage>();
using (var client = new HttpClient())
{
Parallel.ForEach(requests, new ParallelOptions { MaxDegreeOfParallelism = 10 }, async r =>
{
var requestMessage = new HttpRequestMessage();
requestMessage.Method = HttpMethod.Post;
requestMessage.Content = new StringContent(r, Encoding.UTF8, "application/json");
var response = await client.SendAsync(requestMessage);
responses.Add(response);
});
}
}
}
I am mocking HttpClient for unit testing following this SO suggestion. It works as expected for a single endpoint, and I am wondering how I can adjust it for a different endpoint. Please see the following sample:
class Agent
{
private HttpClient _client;
private string _baseUri = "http://example.com/";
public Agent(HttpClient client)
{ _client = client; }
public bool Run()
{
var res1 = client.GetAsync(new Uri(_baseUri, "endpoint1"));
var res2 = client.GetAsync(new Uri(_baseUri, "endpoint2"));
return res1 == res2
}
}
// In the test method:
var responseMessage = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("test_return")
};
var mock = new Mock<HttpMessageHandler>();
mock.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(responseMessage);
client = new HttpClient(mock.Object);
var agent = new Agent(client);
var resp = agent.Run();
Assert.True(resp)
In the above example, resp will be always true because as a result of mocking, the response from both endpoints in the Run method will be equal.
I think that you should set up two SendAsync calls as in the following.
// response to /endpoint1
var responseMessage1 = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("test_return")
};
// response to /endpoint2
var responseMessage2 = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("test_other_return")
};
var mock = new Mock<HttpMessageHandler>();
// mock a call to /endpoint1
mock.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.Is<HttpRequestMessage>(
m => m.RequestUri.AbsolutePath.Contains(
"endpoint1")),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(responseMessage1);
// mock a call to /endpoint2
mock.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.Is<HttpRequestMessage>(
m => m.RequestUri.AbsolutePath.Contains(
"endpoint2")),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(responseMessage2);
var client = new HttpClient(mock.Object);
var agent = new Agent(client);
var resp = agent.Run();
After reading other answers I can't realize why SendAsync is so slow.
Calling same endpoint from Postman, I got a response in 160ms.
Calling from the code below, takes 10 seconds. I'm using a c# desktop application to make the call.
public static async Task<string> GetToken()
{
var url = "....";
var dict = new Dictionary<string, string>();
dict.Add("username", "foo");
dict.Add("password", "bar");
using (var client = new HttpClient(
new HttpClientHandler
{
Proxy = null,
UseProxy = false
}))
{
//bypass SSL
ServicePointManager.ServerCertificateValidationCallback = new
RemoteCertificateValidationCallback
(
delegate { return true; }
);
var req = new HttpRequestMessage(HttpMethod.Post, url) { Content = new FormUrlEncodedContent(dict) };
var res = await client.SendAsync(req); //10 seconds here!
if (res.StatusCode != HttpStatusCode.OK)
return string.Empty;
var token = await JsonConvert.DeserializeObject<TokenResponse>(res.Content.ReadAsStringAsync());
return token.access_token;
}
}
Your code is tangled and ignores IDisposable and this: HttpClient is intended to be instantiated once per application, rather than per-use.
Make reusable method for other-type requests
private static readonly HttpClient client = new HttpClient();
private async Task<T> PostDataAsync<T>(string url, Dictionary<string, string> formData)
{
using (HttpContent content = new FormUrlEncodedContent(formData))
using (HttpResponseMessage response = await client.PostAsync(url, content).ConfigureAwait(false))
{
response.EnsureSuccessStatusCode(); // throws if 404, 500, etc.
string responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
return JsonConvert.DeserializeObject<T>(responseText);
}
}
Usage
public static async Task<string> GetToken()
{
var url = "....";
var dict = new Dictionary<string, string>();
dict.Add("username", "foo");
dict.Add("password", "bar");
try
{
TokenResponse token = await PostDataAsync<TokenResponse>(url, dict);
return token.access_token;
}
catch (HttpRequestException ex)
{
// handle Exception here
return string.Empty;
}
}
I want to add Employee on BamBhooHr using Bambhoohr REST API in MVC C#.
I have tried 2 content types to post as shown in the code but not succeed.
1==>
public async System.Threading.Tasks.Task<JsonResult> AddEmployee(string fn,string ln)
{
var _resultModel = new BBHEmployee();
var _bambhoohrApi = "https://epicsoftsandbox.bamboohr.com/api/gateway.php/epicsoftsandbox/v1/employees";
var _apiKey = "b2aef724a48603468bfe85dce9e417ac8cf15fdf";
var _url = $"{_bambhoohrApi}";
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(_apiKey + ":x");
var base64encodedData = System.Convert.ToBase64String(plainTextBytes);
using (var _client = new HttpClient())
{
var _postData = new Dictionary<string, string>
{
{ "firstName", fn },
{ "lastName", ln }
};
_client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", base64encodedData);
var _response = await _client.PostAsync(_url, new FormUrlEncodedContent(_postData));
var _content = await _response.Content.ReadAsStringAsync();
_resultModel = JsonConvert.DeserializeObject<BBHEmployee>(_content);
}
return Json(_resultModel, JsonRequestBehavior.AllowGet);
}
2==>
public async System.Threading.Tasks.Task<JsonResult> AddEmployee(string fn,string ln)
{
var _resultModel = new BBHEmployee();
var _bambhoohrApi = "https://epicsoftsandbox.bamboohr.com/api/gateway.php/epicsoftsandbox/v1/employees";
var _apiKey = "b2aef724a48603468bfe85dce9e417ac8cf15fdf";
var _url = $"{_bambhoohrApi}";
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(_apiKey + ":x");
var base64encodedData = System.Convert.ToBase64String(plainTextBytes);
using (var _client = new HttpClient())
{
_client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", base64encodedData);
_client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var jsonString = "{\"firstName\":\"" + fn + "\",\"lastName\":\"" + ln + "\"}";
StringContent _contt = new StringContent(jsonString, Encoding.UTF8, "application/json");
var _response = await _client.PostAsync(_url, _contt);
var _content = await _response.Content.ReadAsStringAsync();
_resultModel = JsonConvert.DeserializeObject<BBHEmployee>(_content);
}
return Json(_resultModel, JsonRequestBehavior.AllowGet);
}
with both ways, it returns StatusCode 400(BadRequeest)
in that case, this BambhooHr API endpoint requires XML data to post,
so I use this and it's working fine.
var xmlString = "<employee><field id = \"firstName\">"+fn+"</field><field id = \"lastName\">"+ln+"</field></employee>";
var _contt = new StringContent(xmlString,Encoding.UTF8, "text/xml");
var _response = await _client.PostAsync(_url, _contt);
I am using REST call, the code is like:
public bool restfunc(string id)
{
var result = Task.Factory.StartNew(() => RunAsync(id)).Result; //RunAsync(id).Wait();
return true;
}
public static Task RunAsync(string id)
{
try
{
dynamic result = null;
string pathValue = WebConfigurationManager.AppSettings["R2G2APIUrl"];
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(pathValue);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.Timeout = TimeSpan.FromMinutes(5);
Task responsetask = null;
var jobid = id.Split('_')[1];
client.GetAsync("OnTranscriptionStarted/" + jobid).ContinueWith((requesttask) =>
{
responsetask = requesttask;
HttpResponseMessage resp = requesttask.Result; //HERE ERR OCCUR
resp.EnsureSuccessStatusCode();
resp.Content.ReadAsStringAsync().ContinueWith((readtask) =>
{
result = JsonConvert.DeserializeObject(readtask.Result);
});
});
}
return result;
Doubt:
I have API in pathvalue..I have to write pathvalue in
GetAsync() or function name and id which I want to call?
Help me out from this problem please..
You can use UriBuilder for that:
var builder = new UriBuilder(
"http", pathValue, 80, string.Format("OnTranscriptionStarted/{0}", jobId));
client.GetAsync(builder.Uri.ToString());