I have a Web Api that invokes another web api call to get some information. In order to make the app more resilient, I implemented a HttpTransientErrorDetectionStrategy following the steps at: https://alexandrebrisebois.wordpress.com/2013/02/21/defining-an-http-transient-error-detection-strategy-for-rest-calls/
After that, I use code like below to invoke the other web app:
RetryPolicy _retryPolicy = new RetryPolicy<HttpTransientErrorDetectionStrategy>(
new ExponentialBackoff(retryCount: 2, minBackoff: TimeSpan.FromSeconds(0), maxBackoff: TimeSpan.FromSeconds(10), deltaBackoff: TimeSpan.FromSeconds(2)));
var _httpClient = new HttpClient
{
BaseAddress = new Uri("http://www.microsoft.com")
};
HttpResponseMessage response = _retryPolicy.ExecuteAsync(async () => await _httpClient.GetAsync($"", HttpCompletionOption.ResponseContentRead)).Result;
The _httpClient.GetAsync call gets stuck, and I have no idea why. If I remove the _retryPolicy, and just use _httpClient.GetAsync directly, it returns in a matter of seconds.
I have similar code on a console app, to invoke the same web app, and that is working fine, so this seems to be specific to the way I am using it in my web API. This is intended to be an app on Azure, but it happens when I debug locally as well. Does anybody have any idea why this is getting stuck? How can I debug this?
Thank you!
I have similar code on a console app, to invoke the same web app, and that is working fine, so this seems to be specific to the way I am using it in my web API.
The code you posted is blocking right here:
HttpResponseMessage response = _retryPolicy.ExecuteAsync(...).Result;
Don't block on async code. Instead, use await:
HttpResponseMessage response = await _retryPolicy.ExecuteAsync(...);
If I remove the _retryPolicy, and just use _httpClient.GetAsync directly, it returns in a matter of seconds.
If your original code is blocking, and you must block on asynchronous code (for some reason), then you can either use the ConfigureAwait(false) hack:
HttpResponseMessage response = _retryPolicy.ExecuteAsync(async () => await _httpClient.GetAsync($"", HttpCompletionOption.ResponseContentRead).ConfigureAwait(false)).Result;
or elide async/await:
HttpResponseMessage response = _retryPolicy.ExecuteAsync(() => _httpClient.GetAsync($"", HttpCompletionOption.ResponseContentRead)).Result;
P.S. Check out DecorrelatedJitterBackoffV2.
Related
I am using Hangfire to execute recurring jobs in my web API and I use System.Web.HttpContext.Current.Server.MapPath in the "RoutineMethod" function.
But the problem is it throws object null exception.
I searched the problem and found that recurring jobs don't use http threads, they use background threads.
Now to resolve this problem I need to call my internal (endpoint) using httpclient.
But to do that I need to give URL of the Web API (to generate URI). So is there any other way to call internal function using httpclient.
My current code:
public static async Task<IHttpActionResult> RoutineTask()
{
//It was like this before.
//await new DemoController().RoutineMethod();
//await new DemoController().RoutineMethod2();
//I am planning to do this.
using (var client = new HttpClient())
{
//But I need to give URI which I don't think is a good idea.
var uri = new Uri("http://localhost/DemoApp/api/DemoController/RoutineMethod");
await client.GetAsync(uri);
}
return new DemoController().Ok();
}
The short answer is no. The HttpClient, as its name implies, requires an http connection.
There are no smell issues with storing service connection information in a configuration file.
However, to expand on Nkosi's comment, it appears that since your code can create an instance of DemoController, that it must have a reference to that controller's project or even be in the same project. I would extract the interesting code into a library or service that all areas needing the information can reference.
Is there an API that allows access to Google's Mobile Friendly Test which can be seen at https://www.google.com/webmasters/tools/mobile-friendly/?
If you can't find one by googling, it probably doesn't exist.
A hacky solution would be to create a process with PhantomJS that inputs the url, submits it, and dirty-checks the dom for results.
PhantomJS is a headless WebKit scriptable with a JavaScript API.
However, if you abuse this, there is a chance that google will blacklist your ip address. Light use should be fine. Also be aware that google can change their dom structure or class names at any time, so don't be surprised if your tool suddenly breaks.
Here is some rough, untested code...
var url = 'https://www.google.com/webmasters/tools/mobile-friendly/';
page.open(url, function (status) {
// set the url
document.querySelector('input.jfk-textinput').value = "http://thesite.com";
document.querySelector('form').submit();
// check for results once in a while
setInterval(function(){
var results = getResults(); // TODO create getResults
if(results){
//TODO save the results
phantom.exit();
}
}, 1000);
});
There is an option in pagespeed api
https://www.googleapis.com/pagespeedonline/v3beta1/mobileReady?url={url}&key={api key}
key can be obtained form google cloud platform.
Acquire a PageSpeed Insights API KEY in https://console.developers.google.com/apis/api/pagespeedonline-json.googleapis.com/overview?project=citric-program-395&hl=pt-br&duration=P30D and create a credentials, follow the google's instructions.
In C# (6.0) and .NET 4.5.2, I did some like this:
(add in your project a reference for Newtonsoft.Json.)
String yourURL = "https://www.google.com.br";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("https://www.googleapis.com");
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync($"/pagespeedonline/v3beta1/mobileReady?url={yourURL }&key=AIzaSyArsacdp79HPFfRZRvXaiLEjCD1LtDm3ww").Result;
string json = response.Content.ReadAsStringAsync().Result;
JObject obj = JObject.Parse(json);
bool isMobileFriendly = obj.Value<JObject>("ruleGroups").Value<JObject>("USABILITY").Value<bool>("pass");
There is an API (Beta) for the Mobile Friendly-Test. (Release Date: 31.01.2017).
The API test outputs has three statuses:
MOBILE_FRIENDLY_TEST_RESULT_UNSPECIFIED Internal error when running this test. Please try running the test again.
MOBILE_FRIENDLY The page is mobile friendly.
3.NOT_MOBILE_FRIENDLY The page is not mobile friendly.
Here are more informations: https://developers.google.com/webmaster-tools/search-console-api/reference/rest/v1/urlTestingTools.mobileFriendlyTest/run
I've created one Asp.Net Web API project which uses Azure CloudBlockBlob to download some text asynchronous, but the call never returns.
I tried to run the same code in Console App and it works fine.
Following is the code which has issue:-
public async Task<string> DownloadTextAsync(IBlobRef blobRef, CancellationToken cancellationToken)
{
var container = this.GetContainerReference(blobRef);
try
{
this.Logger.Log("Downloading " + blobRef.BlobName);
return await container.GetBlockBlobReference(blobRef.BlobName).DownloadTextAsync();
}
catch (Microsoft.WindowsAzure.Storage.StorageException e)
{
if (e.RequestInformation.HttpStatusCode == 404)
return null;
Console.WriteLine("Error reading " + blobRef.BlobName + " " + e.Message);
return null;
}
}
This same code if I run in console application or asp.net web form application then it runs perfectly fine.
Kindly help if I'm doing anything wrong.
You're almost certainly calling Task<T>.Result or Task.Wait further up your call stack. This causes a common deadlock issue that I describe on my blog.
The reason it deadlocks is because await (by default) will capture the current "context" - in this case, an ASP.NET request context. Then, when the operation completes, it will resume the async method within that context. However, the ASP.NET request context will only allow one thread in at a time. So, if there's a thread blocked in the ASP.NET request context (e.g., by calling Task.Wait), then the async method cannot resume executing when the await completes.
It doesn't deadlock in a Console app because Console apps do not have a "context". In this case, the async method resumes on a thread pool thread.
To resolve this issue, replace any calls to Result or Wait with await, and allow async to grow through your code. For more information, see my MSDN article on async best practices.
Is there any way to provide RedirectURL then using GoogleWebAuthorizationBroker?
Here is the sample code in C#:
Task<UserCredential> credential = GoogleWebAuthorizationBroker.AuthorizeAsync(secrets, scopes, GoogleDataStore.User, cancellationToken, dataStore);
Or we have to use different approach?
I have an "installed application" that runs on a user's desktop, not a website. By default, when I create an "installed application" project in the API console, the redirect URI seems to be set to local host by default.
What ends up happening is that after the authentication sequence the user gets redirected to localhost and receives a browser error. I would like to prevent this from happening by providing my own redirect URI: urn:ietf:wg:oauth:2.0:oob:auto
This seems to be possible using Python version of the Google Client API, but I find it difficult to find any reference to this with .NET.
Take a look in the implementation of PromptCodeReceiver, as you can see it contains the redirect uri.
You can implement your own ICodeReceiver with your prefer redirect uri, and call it from a WebBroker which should be similar to GoogleWebAuthorizationBroker.
I think it would be great to understand why can't you just use PrompotCodeReceiver or LocalServerCodeReceiver.
And be aware that we just released a new library last week, so you should update it to 1.9.0.
UPDATE (more details, Nov 25th 2014):
You can create your own ICodeReceiver. You will have to do the following:
* The code was never tested... sorry.
public class MyNewCodeReceiver : ICodeReceiver
{
public string RedirectUri
{
get { return YOU_REDIRECT_URI; }
}
public Task<AuthorizationCodeResponseUrl> ReceiveCodeAsync(
AuthorizationCodeRequestUrl url,
CancellationToken taskCancellationToken)
{
// YOUR CODE HERE FOR RECEIVING CODE FROM THE URL.
// TAKE A LOOK AT THE FOLLOWING:
// PromptCodeReceiver AND LocalServerCodeReceiver
// FOR EXAMPLES.
}
}
PromptCodeReceiver
and LocalServerCodeReceiver.
Then you will have to do the following
(instead of using the GoogleWebAuthorizationBroker.AuthorizeAsync method):
var initializer = new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = secrets,
Scopes = scopes,
DataStore = new FileDataStore("Google.Apis.Auth");
};
await new AuthorizationCodeInstalledApp(
new GoogleAuthorizationCodeFlow(initializer),
new MyNewCodeReceiver())
.AuthorizeAsync(user, taskCancellationToken);
In addition:
I'll be happy to understand further why you need to set a different redirect uri, so we will be able to improve the library accordingly.
When I create an installed application the current PromptCodeReceiver and LocalServerCodeReceiver work for me, so I'm not sure what's the problem with your code.
I am building an app which is already build in Android & other Mobile Platform. Since the App uses REST based Webservices build in JAVA, thus I need to use these Webservice URL. The code uses HttpClient and HttpGet for GET,POST,PUT & DELETE operation in Android . Can anyone guide me where to start with as I am new to this Platform.
You can use HttpWebRequest ( http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest(v=vs.95).aspx ) to make calls to a REST service
I would recommend using the WebClient class for simple HTTP based communication. Here is the basic format I typically use when making a request to a web service:
WebClient web = new WebClient();
web.OpenReadCompleted += new OpenReadCompletedEventHandler(RequestComplete);
web.OpenReadAsync(new Uri("http://fullurlofyourwebservice.com"));
You can then write a method for the RequestComplete method referenced in the second line of code:
void RequestComplete(object sender, OpenReadCompletedEventArgs e)
{
string response = "";
using (var reader = new StreamReader(e.Result))
{
response = reader.ReadToEnd();
}
}
You can then process the response as a simple string, or do something like XDocument.Parse(response) if your response is in XML format.
Check out the full MSDN documentation for a complete reference.
I've recently started using RestSharp. http://restsharp.org/
Small, simple and does what it says on the box.