Calling a Web api from another Web api - c#

Is it somehow possible to make a Web api that calls another web api?
I am using the code below to access a web api from my web api, but it never return from the call. If I use the code from a console app, it is working fine.
public void DoStuff(){
RunAsync().Wait();
}
public static async Task RunAsync(){
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:53452/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET
HttpResponseMessage response = await client.GetAsync("umbraco/api/Member/Get?username=test");
if (response.IsSuccessStatusCode)
{
string user = await response.Content.ReadAsAsync<string>();
}
}

I also went through the same problem, after much research I discovered that the await operator does not stop the work if the HttpClient returns error 500. To work around the problem I used Task.Wait().
var response = client.GetAsync ("umbraco/api/Member/Get?username=test");
response.Wait ();
I hope this helps others.

Yes you can make a call to a remote web api within the action method of a web api controller.
Lets eliminate the obvious first.
If you set a breakpoint at the start of this action method it is getting hit right? If not then the issue lies in the routing not the action method.
If you set a breakpoint at the if statement does it get hit or is the client.GetAsync() call never returning?
If you haven't done already you may wish to use a tool like fiddler (http://www.telerik.com/fiddler) to compare the request & response from a working use of the api and this broken one. I know you said it is identical to a working implementation but I have found fiddler invaluable to verify exactly what is being sent "on the wire".

Related

.net HttpClient in C#: Exception when re-instantiating

Am querying multiple APIs with thousands of requests. Thus, I am looping over the end points and the requests. As it is suggested to re-use HttpClient instances, that's what I am doing. However, I need to set some parameters like timeouts, passwords etc. in the header for each API. Thus, the first API works perfectly, when trying to set the Parameters for the next API, it fails:
This instance has already started one or more requests. Properties can only be modified before sending the first request.
Generally I know that the properties need to be set before making any requests. So I considered resetting the HttpClient for each API and then just re-use it for the thousands of requests to that API. Surprisingly, I get the same error - and I have absolutely no idea why.
This is about what the code looks like:
private HttpClient ApiClient;
private List<Api> Endpoints;
[...]
foreach(Api api in this.Endpoints)
{
this.ApiClient = new HttpClient();
this.ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(api.mediaType));
this.ApiClient.Timeout = TimeSpan.FromMinutes(api.timeout);
this.ApiClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", api.credentials);
foreach (string url in api.urls)
{
# retrieve data from APIs and do something with it
}
}
As mentioned earlier, the first loop works perfectly fine. But when it starts over with the second api, I get a System.InvalidOperationException with the error message above when I try to set the ApiClient's timeout value.
Why so? I have created a brand new instance of HttpClient. Is there a better way to just reset the HttpClient?
The preferred way for generating HttpClients seems to be httpfactory: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1. Also note that reinstatiating httpclients as you are, even without your specific exception can lead to problems, as your code seems to be able to run into socket exhaustion as described in https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests.
You should have only one instance of HttpClient during the lifetime of your application.
So instead of creating a HttpClient and setting the DefaultRequestHeaders every time you loop over your endpoints use HttpRequestMessage and do the following:
this.ApiClient = new HttpClient();
foreach(Api api in this.Endpoints)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "url");
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("header value"));
var data = await ApiClient.SendAsync(request , HttpCompletionOption.ResponseContentRead);
}

API GET Request Returns HTML/Text instead of JSON

This is my first time working with a RESTful API and Xamarin and etc. I have so far made a simple REST API. I have written a GET call to it that, if I write http://localhost:[num]/api/Name, it will return a JSON file of the matching Emu's information. I have tested this with Postman, so I know that it works.
I have now written an app that will call this API in order to catch this information and then display it. So far, I've got it connected to the server hosting my API, but I'm unable to get it to return JSON. Instead it seems to be returning text/HTTP.
From what I've searched up on previous Stack Overflow threads, it seems that I was missing Headers requesting that reply be in a JSON format. When I added in code that was on the official .NET documentation on Microsoft's website, it gave me issues with my Json Deserialiser. I have also added in the information in the header to make sure that it returns json.
Here is the code for the function:
async private void Submit_OnClicked(object sender, EventArgs e)
{
var nameValue = EmuName.Text;
var baseAddr = new Uri("http://my_url/HelloEmu/");
var client = new HttpClient { BaseAddress = baseAddr };
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
string url = (string)nameValue;
var returnedJson = await client.GetStringAsync(url);
Models.EmuItemModel MyEmu = JsonConvert.DeserializeObject<Models.EmuItemModel>(returnedJson);
ReturnedName.Text = MyEmu.Name;
ReturnedAge.Text = MyEmu.Age.ToString();
ReturnedWeight.Text = MyEmu.Weight.ToString();
My code actually faults on the line ReturnedWeight.Text = MyEmu.Weight.ToString()
But I'm guessing the more majour issue is occuring during deserialisng the object, because it seemingly "skips" over the preceeding two lines when I run it in the debugger.
When I run it in Visual Studio 2019, the value of "returnedJson" is this:
"<html><head><meta http-equiv=\"refresh\" content=\"0;url=http://lookup.t-mobile.com/search/?q=http://my_url/HelloEmu/Keith&t=0\"/></head><body><script>window.location=\"http://lookup.t-mobile.com/search/?q=\"+escape(window.location)+\"&r=\"+escape(document.referrer)+\"&t=0\";</script></body></html>"
I think this is an HTML output. I would love any hints about what on earth I'm doing wrong!
EDIT: Since it almost seems like the HTML is returning an error message, perhaps it could do with my url??? I've published the website using the File system method. So to access the API in Postman I'll use http://localhost:[port]/api/values, calling my website in a regular ol' browser makes it go http://my_url/HelloEmu. I get a 403 "no directory" method in return...
EDIT: Here is the Postman code:
enter image description here
Usually it happens because there are missing headers or some other malformed request, Download RestSharp DLL from NuGet, and then you can use the following, in postman, go to "Code":
And choose C# you will see a code snippet (Example):

HttpClient PostAsync does not return

I've seen a lot of question about this, and all points to me using ConfigureAwait(false), but even after doing so, it still doesn't returned any response. When I run the debugger, the code stops at the PostAsync and does not continue with my code. Am I doing something wrong here? Does it have to do with me calling an API via HTTPS?
Here's the code:
public async static Task<PaymentModel> AddAsync(Card card)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:", "hidden"))));
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
var cardJson = JsonConvert.SerializeObject(card);
var postRequest = new StringContent(cardJson, Encoding.UTF8, "application/json");
var request = await client.PostAsync(new Uri("https://sample-3rd-party-api/api/endpoint/here"), postRequest).ConfigureAwait(false);
var content = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
}
EDIT:
In response to the comments below, the code is contained from a method AddAsync(Card card) called from a button click with a handler:
public async void OnExecute(object sender, EventArgs args)
{
//some code here
payment = await PaymentModel.AddAsync(card).ConfigureAwait(false);
}
EDIT 2:
I tried pinging the API, but it returns a request timed out, but when I tried it using Postman, it's doing fine (the API is just a Sandbox which is open for all, so it's okay to share this):
EDIT 3:
I think the problem lies with where I don't have an SSL certificate to access the API. I have a PHP server that connects to the same API and I have to set SSL_VERIFYPEER to false just so I can access it (don't worry, I added a cacert now so its on true again). Can the same issue be happening here? If so, what can I do to create a valid certificate for my xamarin forms app
You can use this
var json = JsonConvert.SerializeObject(card);
using (var client = new HttpClient())
{
var t = await client.PostAsJsonAsync("https://sample-3rd-party-api/api/endpoint/here", json);
Response R =JsonConvert.DeserializeObject<Response>((JsonConvert.DeserializeObject(t.Content.ReadAsStringAsync().Result.ToString())).ToString());
}
What's most likely happening here is your OnExecute method has a return type of void instead of Task which prevents the UI thread from being able to await it. Try either changing that return type to Task or creating a new UI thread to perform this work. I wouldn't worry about the ping timing out as long as Postman works. Many public web servers disable their ping response.
Does it have to do with me calling an API via HTTPS?
As you are remaining in the same network and calling the same API from POSTMAN and .NET HTTP Client and only getting success with POSTMAN.So this issue gets cancelled.
Next
tried pinging the API, but it returns a request timed out
This is answered on top of mine.
Can you Please try setting the timeout option for HTTPClient while initializing.
client.Timeout = TimeSpan.FromSeconds(10);
and if still Problem persists please setup Fiddler and compare both the req sent from POstman and .NET client
So I think the problem is resolved now since I'm able to receive content from the request, what I did was simply follow the docs here: https://learn.microsoft.com/en-us/xamarin/cross-platform/app-fundamentals/transport-layer-security?tabs=windows
It looks like my settings are outdated in platform level.
Update the HttpClient implementation and SSL/TLS implementation
options to enable TLS 1.2 security.
Update the HttpClient Implementation option to enable TSL 1.2
security. (NSUrlSession (iOS 7.0+)
I was having the same issue and below trick fixed the issue.
Change your var request = await client.PostAsync(...); as below
var task = client.PostAsync(new Uri("https://sample-3rd-party-api/api/endpoint/here"), postRequest);
var request = task.GetAwaiter().GetResult();

How to make HTTP calls, using ASP.NET MVC?

What I'm trying to do:
I'm trying to practise making HTTP calls (...if that is what it's called) from a simple ASP.NET MVC web application. To do this, I am attempting to get weather details from OpenWeatherMap. You can do this by:
Add the following parameter to the GET request: APPID=APIKEY
Example: api.openweathermap.org/data/2.5/forecast/city?id=524901&APPID=1111111111
My understanding, from my learning:
The controller is the one to make the above HTTP call.
My question:
How do I actually make that HTTP GET request, in ASP.NET MVC?
Use System.Net.Http.HttpClient.
You can do some basic reading from a website using something like the following:
using (var client = new HttpClient())
{
var uri = new Uri("http://www.google.com/");
var response = await client.GetAsync(uri);
string textResult = await response.Content.ReadAsStringAsync();
}
You may want to make sure to test response.IsSuccessStatusCode (checks for an HTTP 200 result) to make sure the result is what you expect before you parse it.

WebAPI Response never completes when returning HttpResponseMessage and Application Insights is configured

I have an MVC5/WebAPI2 application which has had Application Insights enabled since I created the web project.
WebApi methods that return objects (e.g. string, model objects) are returned as expected - serialized into JSON or XML.
public class TestController : ApiController
{
[HttpGet]
[AllowAnonymous]
async public Task<HttpResponseMessage> ReadString(int id) {
HttpResponseMessage response = Request.CreateResponse();
string str;
using (HttpClient client = new HttpClient()) {
Uri uri = new Uri("http://someurl.com/resource.rss");
str = await client.GetStringAsync(uri);
}
response.Content = new StringContent(str);
response.Content.Headers.ContentLength = str.Length;
return response;
}
}
When I created an action that returns an HttpResponseMessage, I noticed a strange behavior in the browser (tested with Chrome and IE). Specifically, my content was returned to the browser, but the "busy" indicator never stopped spinning until I hit the Stop button, or stopped the web server (Visual Studio 2013).
I've verified that the method above works as expected in a web app without Application Insights. Specifically, once the data is returned to the browser, the response ends. When the method below is added to a freshly-created app with Application Insights, the behavior described previously is encountered.
Any thoughts?
This is caused by a NullReferenceException thrown in the ASP.NET request pipeline. The Microsoft.ApplictionInsights.Web package is handling the HttpApplication.PreSendRequestHeaders event, which triggers the problem. In the upcoming 0.17 release we have changed the Application Insights code to no longer handle this event and you shouldn't see this problem anymore.

Categories

Resources