C# HttpClient Bad Request on second call - c#

I've found with the below code that on any second httpclient request, I always get a 400 Bad Request.
I can confirm the API calls actually work and return a 200 - so if I flip them, the first call will return 200 second call, 400.
I can't figure out why this is the case.
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(BaseUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage blah = await client.GetAsync("api/subscriptions?account=eclipse&application=Sample");
HttpResponseMessage dfds = await client.GetAsync("api/subscriptions?account=eclipse");
...
}

It's hard to tell without seeing the controller function you're trying to call. What's probably happening is that the REST service is experiencing an exception on the server and is throwing back a 400.
What web server is the service using? Maybe it has some sort of simultaneous request blocking. You could try not using async.
What I would do is open up Fiddler (http://www.telerik.com/fiddler) and look at the network traffic. Look at the response from the web server and see if there is any exception data there.

Related

Adding Session Request To API Call

I'm unsure how to add the Session Request to the API call as per the Food Hygiene Instructions. I've copied the relevant code and hope I am close, but unsure where to put this one part.
Reference: https://api.ratings.food.gov.uk/help
Need to add into the API call: Session.Request.Headers.Add("x-api-version", 2);
Partial Code:
readonly string Baseurl = "https://api.ratings.food.gov.uk";
public async Task<ActionResult> Index()
{
List<Authorities> AuthInfo = new List<Authorities>();
using var client = new HttpClient
{
//Passing service base url
BaseAddress = new Uri(Baseurl)
};
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
***//Session.Request.Headers.Add("x-api-version", 2);***
//Sending request to find web api REST service resource GETRegions using HttpClient
HttpResponseMessage Res = await client.GetAsync("Authorities/basic");
The documentation you're referring to is misleading/incorrect. When you talk about Session (at least in the .NET world) you talk about the server-side. You're obviously a client of the API, rather than a developer of it, so they asking you to put stuff in the Session is incorrect.
You're a client, passing headers in your requests, so it's just:
client.DefaultRequestHeaders.Add("x-api-version", "2");
Side note, you may want to reuse that HttpClient instance if you are going to make that call often.
Side note 2: you may want to ask them to fix the docs :)

Timeout while making POST request with no authentication C#

I want to make a POST request to a rest service. There is no authentication, it has only two customized header. My code is below. I am getting the error :
An exception of type 'System.AggregateException' occurred in mscorlib.dll but was not handled in user code.
"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond"
May you help ? What is wrong in the code ?
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("id", "8888");
client.DefaultRequestHeaders.Add("type", "CUSTOMER");
Uri uri = new Uri(requestUri);
var ob = new { id= "5", color= "pink" };
var transferJson = JsonConvert.SerializeObject(ob);
var content = new StringContent(transferJson, Encoding.UTF8, "application/json");
HttpResponseMessage responseMessage = client.PostAsync(uri, content).Result;
Your code itself doesn't look faulty. The error message suggests that the request ran into a timout, which means that the HttpClient waits for a set period of time and terminates if the server doesn't respond. Have you tried pinging the server to make sure it's actually up and running?
It that's the case you could try to increase the timeout value of your HttpClient (see here https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.timeout?view=netframework-4.8).
Additionally you could try to send the request with another tool like Postman to see whether the issue lies within your code, your parameters (like timeout), or the server itself.

web api Bad Request when getting access token after moving to production

I have a web api that is working great in test using an access token / bearer authentication. I authenticate and make requests using HttpClient. Easy.
Here is the basic web client setup. The base address is a constant that I change when moving to production.
public static HttpClient GetClient()
{
HttpClient Client = new HttpClient();
Client.BaseAddress = new Uri(Ics2Constants.ICS2APIBaseAddress);
Client.DefaultRequestHeaders.Accept.Clear();
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return Client;
}
I build the token request login info like so:
var values = new Dictionary<string, string>();
values.Add("grant_type", "password");
values.Add("username", "niceUser");
values.Add("password", "NiCePaSsWord");
var loginContent = new FormUrlEncodedContent(values);
And then I make the request for the access token:
var loginResponse = await client.PostAsync("/Token", loginContent);
In test mode, perfect. I can get my access token, pass it back on subsequent requests. All is good.
When I move to production. I get a bad request 400 on the request for access token. I do have the base address right because if I take off the authorize attribute I can get data back.
Something is different about the request for access token in production, but I have no clue what to change.
Well, the answer ended up being two part:
1) Issue with the web host. They had a corruption on their end.
2) After they fixed their issue I still received a 404 (not found)... so I had to take out the "/" in my PostAsync. So the new line looks like so:
var loginResponse = await client.PostAsync("Token", loginContent);
It worked in debug on the local side with the "/", but the production side was not happy.
I'm up and working now. :)

Web API login crash

I am able to use the accounts built in to web api to register a new user. This works fine. However when I come to then log on with any username at all, the process just hangs, never returns and locks my UI. Can someone see anything wrong here? The commented line is to show what I have also tried.
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
//client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
client.BaseAddress = new Uri(ApiUrl);
And then to send a logon reaquest...
var response = client.PostAsync("Token", new StringContent("grant_type=password&username=xxx&password=xxx1234", Encoding.UTF8)).Result;
It then hangs on this line above, and never returns. I don't even know which controller method on the Web API to breakpoint because there is no obvious one. I have tried all methods in the AccountController.cs and the ApplicationOAuthProvider.cs. I dont even think it gets this far.
I have tried the same method I use for registering a new user and this just returns "Bad Request".
HttpResponseMessage response = await client.PostAsJsonAsync("Token", logonObject);
Any pointers would be great!
You're running into a common deadlock situation, due to the use of Task<T>.Result. I explain the deadlock fully on my blog, but the general gist is that await will capture a context (in this case, the ASP.NET request context) and use that to resume its async method. If you block a thread in the ASP.NET request context (e.g., by calling Task<T>.Result), then the async method cannot complete.
The proper solution is to replace Result with await:
var response = await client.PostAsync("Token", new StringContent("grant_type=password&username=xxx&password=xxx1234", Encoding.UTF8));

Asp.net Web Api Redirect request

I have an Asp.net Web API project which has several CRUD methods.
On top of these methods, i want to add an Authorization service that reads the Authorization header and prevent users of accessing the resources (if they are not authorized).
// Method on internal IP Project
public class InternalController : ApiController
{
public void Create(CreateRequest request)
{
// implement the method
}
}
// Method on public IP Project
public class ExternalController : ApiController
{
public async Task Create(CreateRequest request)
{
// validate Authorization header and throw exception if not valid
using (HttpClient client = new HttpClient())
{
string parameters = string.Format("param1={0}&param2={1}", request.Param1, request.Param2);
client.BaseAddress = new Uri("http://192.168.1.1/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/internal/create?" + parameters);
response.EnsureSuccessStatusCode();
}
}
}
Is there any way of "Redirecting" the request from the External API to the Internal API more easily?
Right now, i have to manually re-create all the parameters that i receive in ExternalAPI and send them in the InternalAPI, even if they are the same.
Can i make HttpClient automatically send the HttpRequestMessage (Request) object that i have in ExternalAPI method?
When speaking about ASP.NET Web API. HttpClient will not automatically redirect you. When you have become response from internal service you can pass it to external. Or you can redirect your action like here
To make it correct redirection for client from REST point of view use HTTP Redirect Headers and repsonse codes. For example HTTP response code 302. And then client should be able to react on such response code and get redirect address from Location header. But it's about redirect for client.
When speaking about call of some internal services from your API from architecture. You have following alternatives:
Call your internal service as class method
Make service to service call
Setup message queue or bus and your API will communicate with it through service bus.
Call your internal service as class method
Very easy. No impact and delays for service call. But you should reference assembly and it's not always possible. Or such way could be not possible due to requirements
Make service to service call
Has disadvantages: your services are tightly coupled, you have delay and should wait for response from internal service. It's considered as bad practice. But could be a good temporarily solution as first step to service bus.
Setup message queue or bus and your API will communicate with it through service bus.
Your services are decoupled and independent. You shouldn't wait for response. But it's technically harder to set up and make your architecture and infrastructure more complex/
As summary
There is no best way from the box for your architecture and you should select from alternatives based on your requirements.
Here is the sample code to Post the data to web api:-
var handler = new HttpClientHandler {UseDefaultCredentials = true};
using (var client = new HttpClient(handler))
{
client.BaseAddress = new Uri("https://IE url.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var postDataObject = new
{
SCName = properties.Site.PortalName,
TotalSites = properties.Web.Webs.Count
};
var jsonPostData = new JavaScriptSerializer().Serialize(postDataObject);
HttpContent content = new StringContent(jsonPostData, System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = client.PostAsync("/controllerclassname/InsertUpdateDataOperation", content).Result;
if (response.IsSuccessStatusCode)
{
//Check the response here
// var webApiResponse = response.Content.ReadAsStringAsync().Result;
}

Categories