Web API login crash - c#

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));

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 :)

Need help getting JSON post working for EpiServer CMS version higher than 8.0.0

We have multiple EpiServer sites where we are adding the ability to post JSON to a site monitoring API. I was able to get the JSON posts successfully working on our EpiServer CMS version 8.0.0 sites, but encountered problems with CMS versions 8.8.1 and higher.
Below is what our successful working code looks like.
private async Task SendMaintenanceEvent(object maintenanceEvent)
{
string endpoint = "https://OurEndpointURL.com/omitted/";
string endpointDirectory = "target";
// Provide basic authorization. Credentials must be base-64 encoded to be recognized.
string credentials = "AuthCredentialsOmitted";
string credentialsBase64 = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(credentials));
// Convert the maintenanceEvent object to consumable JSON.
string maintenanceEventJson = System.Web.Helpers.Json.Encode(maintenanceEvent);
StringContent content = new StringContent(maintenanceEventJson, Encoding.UTF8, "application/json");
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(endpoint);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentialsBase64);
HttpResponseMessage response = await httpClient.PostAsJsonAsync(endpointDirectory, content);
if (!response.IsSuccessStatusCode)
{
throw new System.Exception("Error sending maintenance event.");
}
}
}
The above method depends on a couple of using statements are also in this class.
using System.Net.Http;
using System.Net.Http.Headers;
The above succeeds in our EpiServer CMS 8.0.0 solutions. But when we port the same code over to one of the higher CMS versions, the posts get stuck at this line:
HttpResponseMessage response = await httpClient.PostAsJsonAsync(access.EndpointDirectory, content);
By "gets stuck" I mean the Visual Studio debugger stops on that line and never proceeds to the following line.
Researching this, I found a suggestion to use PostAsync instead of PostAsJsonAsync. So here is one of my attempts on an EpiServer 9 solution. But this ends up posting as text/plain instead of as application/json.
private async Task SendMaintenanceEvent(object maintenanceEvent)
{
string endpointAddress = "https://OurEndpointURL.com/omitted/";
string endpointDirectory = "target";
// Provide basic authorization. Credentials must be base-64 encoded to be recognized.
string credentials = "AuthCredentialsOmitted";
string credentialsBase64 = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(credentials));
// Convert the maintenanceEvent object to consumable JSON.
//string maintenanceEventToPost = System.Web.Helpers.Json.Encode(maintenanceEvent);
//StringContent stringContent = new StringContent(maintenanceEventToPost, Encoding.UTF8, "application/json");
string jsonMaintenanceEvent = JsonConvert.SerializeObject(maintenanceEvent);
StringContent stringContent = new StringContent(jsonMaintenanceEvent, Encoding.UTF8, "application/json");
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(endpointAddress);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentialsBase64);
HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(endpointDirectory, stringContent);
if (!httpResponseMessage.IsSuccessStatusCode)
{
throw new System.Exception("Error sending maintenance event to Monitor.");
}
}
}
Comparing the posts in Fiddler, the successful code has a Content-Type of application/json. But the unsuccessful block of code has a Content-Type of text/plain. I thought that Content-Type was based on the StringContent object, and I've set the ContentType as follows:
StringContent stringContent = new StringContent(jsonMaintenanceEvent, Encoding.UTF8, "application/json");
I don't understand why PostAsync disregards that setting. The object has a mediaType of application/json.
And if I change the post from PostAsync to PostAsJsonAsync, the post just gets stuck, as mentioned above.
Ultimately I just need to get the JSON post working in EpiServer versions higher than 8.0.0. After working on this for several days, this is just baffling. Thanks for your help.
I don't know what the calling code looks like but what's happening is a deadlock, you can avoid this by using .ConfigureAwait(false) on your PostAsJsonAsync call like this:
HttpResponseMessage response = await httpClient.PostAsJsonAsync(access.EndpointDirectory, content).ConfigureAwait(false);
You can read more about deadlocks when using async/await here: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
His example looks a lot like your problem.
The top-level method calls GetJsonAsync (within the UI/ASP.NET context).
GetJsonAsync (within the UI/ASP.NET context).
GetJsonAsync starts the REST request by calling HttpClient.GetStringAsync (still within the context).
GetStringAsync returns an uncompleted Task, indicating the REST request is not complete.
GetJsonAsync awaits the Task returned by GetStringAsync. The context is captured and will be used to continue running the GetJsonAsync method later. GetJsonAsync returns an uncompleted Task, indicating that the GetJsonAsync method is not complete.
The top-level method synchronously blocks on the Task returned by GetJsonAsync.
This blocks the context thread.
… Eventually, the REST request will complete. This completes the Task that was returned by GetStringAsync.
The continuation for GetJsonAsync is now ready to run, and it waits for the context to be available so it can execute in the context.
Deadlock. The top-level method is blocking the context thread, waiting for GetJsonAsync to complete, and GetJsonAsync is waiting for the context to be free so it can complete.
Stephen lists 2 ways to avoid deadlocks
In your “library” async methods, use ConfigureAwait(false) wherever possible.
and
Don’t block on Tasks; use async all the way down.
Without seeing the code that actually calls your SendMaintenanceEvent method it's difficult to tell what's actually causing the deadlock.

C# HttpClient Bad Request on second call

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.

Windows store app HttpClient issue

i'm testing some code here and came across a really weird issue. The HttpClient class works like a charm if my tablet is on but whenever the battery safe state kicks in and the screen is locked the HttpClient class throw an exception and the app suddendly exits giving me small chances to log the exception. If i'm not mistaken the error seems to be a send request error but that's weird since i have full access to the internet while the tablet is on.
Here the code i'm using and the one that crash on that state(System.Net.Http namespace ):
private static async Task<string> HttpGet(string uri)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", BotUserAgent);
var response = await client.GetAsync(uri);
return await response.Content.ReadAsStringAsync();
}
}
Alternatively i've used the client of the web.http namespace with the same result:
using(var client = new Windows.Web.Http.HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", BotUserAgent);
var response = await client.GetAsync(new Uri(uri));
return await response.Content.ReadAsStringAsync();
}
This is the error i get(after adding an exception logger):
An error occurred while sending the request.
Any ideas?
The problem is that your WinRT-device sends your app to the background when the battery safe state kiks in.
whenever the user moves your app into the background, your app can be suspended or frozen, essentially stopping any downloads dead in their tracks. In some scenarios, the app might
even be terminated, forcing you to create a new instance of the class in an attempt to start the download again. Fortunately, WinRT provides a way to handle this specific scenario using a background task.
This is a quote from http://www.informit.com/articles/article.aspx?p=2229233&seqNum=6
On this site is an easy tutorial to follow on how to use BackgroundTransfers in the Windows.Networking.BackgroundTransfer-Namespace.

Web Api + HttpClient: An asynchronous module or handler completed while an asynchronous operation was still pending

I'm writing an application that proxies some HTTP requests using the ASP.NET Web API and I am struggling to identify the source of an intermittent error.
It seems like a race condition... but I'm not entirely sure.
Before I go into detail here is the general communication flow of the application:
Client makes a HTTP request to Proxy 1.
Proxy 1 relays the contents of the HTTP request to Proxy 2
Proxy 2 relays the contents of the HTTP request to the Target Web Application
Target Web App responds to the HTTP request and the response is streamed (chunked transfer) to Proxy 2
Proxy 2 returns the response to Proxy 1 which in turn responds to the original calling Client.
The Proxy applications are written in ASP.NET Web API RTM using .NET 4.5.
The code to perform the relay looks like so:
//Controller entry point.
public HttpResponseMessage Post()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
private static HttpRequestMessage BuildRelayHttpRequest(HttpRequestMessage incomingRequest)
{
var requestUri = BuildRequestUri();
var relayRequest = new HttpRequestMessage(incomingRequest.Method, requestUri);
if (incomingRequest.Method != HttpMethod.Get && incomingRequest.Content != null)
{
relayRequest.Content = incomingRequest.Content;
}
//Copies all safe HTTP headers (mainly content) to the relay request
CopyHeaders(relayRequest, incomingRequest);
return relayRequest;
}
private static HttpRequestMessage BuildResponse(HttpResponseMessage responseMessage)
{
var returnMessage = Request.CreateResponse(responseMessage.StatusCode);
returnMessage.ReasonPhrase = responseMessage.ReasonPhrase;
returnMessage.Content = CopyContentStream(responseMessage);
//Copies all safe HTTP headers (mainly content) to the response
CopyHeaders(returnMessage, responseMessage);
}
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
var content = new PushStreamContent(async (stream, context, transport) =>
await sourceContent.Content.ReadAsStreamAsync()
.ContinueWith(t1 => t1.Result.CopyToAsync(stream)
.ContinueWith(t2 => stream.Dispose())));
return content;
}
The error that occurs intermittently is:
An asynchronous module or handler completed while an asynchronous operation was still pending.
This error usually occurs on the first few requests to the proxy applications after which the error is not seen again.
Visual Studio never catches the Exception when thrown.
But the error can be caught in the Global.asax Application_Error event.
Unfortunately the Exception has no Stack Trace.
The proxy applications are hosted in Azure Web Roles.
Any help identifying the culprit would be appreciated.
Your problem is a subtle one: the async lambda you're passing to PushStreamContent is being interpreted as an async void (because the PushStreamContent constructor only takes Actions as parameters). So there's a race condition between your module/handler completing and the completion of that async void lambda.
PostStreamContent detects the stream closing and treats that as the end of its Task (completing the module/handler), so you just need to be sure there's no async void methods that could still run after the stream is closed. async Task methods are OK, so this should fix it:
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
Func<Stream, Task> copyStreamAsync = async stream =>
{
using (stream)
using (var sourceStream = await sourceContent.Content.ReadAsStreamAsync())
{
await sourceStream.CopyToAsync(stream);
}
};
var content = new PushStreamContent(stream => { var _ = copyStreamAsync(stream); });
return content;
}
If you want your proxies to scale a bit better, I also recommend getting rid of all the Result calls:
//Controller entry point.
public async Task<HttpResponseMessage> PostAsync()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
Your former code would block one thread for each request (until the headers are received); by using async all the way up to your controller level, you won't block a thread during that time.
I would like to add some wisdom for anyone else who landed here with the same error, but all of your code seems fine. Look for any lambda expressions passed into functions across the call-tree from where this occurs.
I was getting this error on a JavaScript JSON call to an MVC 5.x controller action. Everything I was doing up and down the stack was defined async Task and called using await.
However, using Visual Studio's "Set next statement" feature I systematically skipped over lines to determine which one caused it. I kept drilling down into local methods until I got to a call into an external NuGet package. The called method took an Action as a parameter and the lambda expression passed in for this Action was preceded by the async keyword. As Stephen Cleary points out above in his answer, this is treated as an async void, which MVC does not like. Luckily said package had *Async versions of the same methods. Switching to using those, along with some downstream calls to the same package fixed the problem.
I realize this is not a novel solution to the problem, but I passed over this thread a few times in my searches trying to resolve the issue because I thought I didn't have any async void or async <Action> calls, and I wanted to help someone else avoid that.
A slightly simpler model is that you can actually just use the HttpContents directly and pass them around inside the relay. I just uploaded a sample illustrating how you can rely both requests and responses asynchronously and without buffering the content in a relatively simple manner:
http://aspnet.codeplex.com/SourceControl/changeset/view/7ce67a547fd0#Samples/WebApi/RelaySample/ReadMe.txt
It is also beneficial to reuse the same HttpClient instance as this allows you to reuse connections where appropriate.

Categories