I have an interesting issue... For my windows phone 8.1 universal app, I call a method (CommAuthState), defined in my class, from my code:
Here is truncated code which is calling this method.
public async void ShowInitialPosts(object sender, RoutedEventArgs e)
{
#if WINDOWS_PHONE_APP
Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
#endif
//Get reference to the App setting container...
Windows.Storage.ApplicationDataContainer appRoamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
//Get the value for the logged in state and remembering option....
string commLoggedInValue = (string)appRoamingSettings.Values["CommLoggedIn"];
//If not logged in, redirect to the Community sign in page...
if (commLoggedInValue != "Yes")
{
this.Frame.Navigate(typeof(CommSignIn));
}
else if (commLoggedInValue == "Yes")
{
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//Check the access token and its validity...
forum_Hepler.CommAuthState(errorMessage, pgbar, pgText, ServerNetworkError);
});
//Get the access token value...
string myAccessTokenValue = (string)appRoamingSettings.Values["MyAccessToken"];
//Set the access token to the page variable...
accesstoken = myAccessTokenValue;
// Show the progress bar
mycontrols.progressbarShow(pgbar, pgText);
//Set the initial forum URL and page index..
downloadedPostsIndex = 0;
forumUrl = forumUrl + downloadedPostsIndex;
// Make a REST API call with Oauth token........
string telligentResult = await forum_Hepler.MyForumPostsOauth(forumUrl, accesstoken, errorMessage);
.....
.......
}
Here is my class method:
public async void CommAuthState(string errormessage, ProgressBar myprogressbar, TextBlock mytextblock, TextBlock myservernetworkerror) {
// Start showing the progress bar...
mycontrols.progressbarShow(myprogressbar, mytextblock);
//Get the access token value...
string myAccessTokenValue = (string) appRoamingSettings.Values["MyAccessToken"];
//Get the refresh token value...
string myRefreshTokenValue = (string) appRoamingSettings.Values["MyRefreshToken"];
//Get the original access token obtain time....
long myAccessTokenObtainedTimeValue = (long) appRoamingSettings.Values["MyAccessTokenObtainedTime"];
//Convertig date/time back to DateTime object....
origAccessTokenObtainedTime = DateTime.FromBinary(myAccessTokenObtainedTimeValue);
currentDateTime = DateTime.Now;
//Check to see if access token has expired....
accessTokenTimeElasped = currentDateTime - origAccessTokenObtainedTime;
accessTokenTimeElapsedSecs = accessTokenTimeElasped.TotalSeconds;
//Get the value of the access token expiration....
int MyAccessTokenExpiresValue = (int) appRoamingSettings.Values["MyAccessTokenExpires"];
if (accessTokenTimeElapsedSecs <= MyAccessTokenExpiresValue) {
//Get the long GUID value to be used for the GetOauthStuff function below...
string myLongGuidValue = (string) appRoamingSettings.Values["MyLongGuid"];
//Make a GET call to the OAUTH endpoint to get another Access Token by sending the refresh token ....
// string telligentOauthResult = await GetOauthStuff(oauthEndPoint, MyLongGuidValue, keyName, keySecret, errormessage);
string telligentOauthResult = await GetRefreshedOauthStuff(oauthEndPoint, myLongGuidValue, myRefreshTokenValue, keyName, keySecret, errormessage);
if (telligentOauthResult != null) {
// Creating a list out of the JSON object returned by the Telligent API endpoint...
ForumHelper.OAuthObject mytelligentResult = JsonConvert.DeserializeObject < ForumHelper.OAuthObject > (telligentOauthResult);
var accessToken = mytelligentResult.OAuthToken.access_token;
// Get the access token and the time and save it to settings
accessTokenObtainedTime = DateTime.Now;
var accessTokenError = mytelligentResult.OAuthToken.error;
var accessTokenTime = mytelligentResult.OAuthToken.expires_in;
var refreshToken = mytelligentResult.OAuthToken.refresh_token;
//Save access token to the app settings...
appRoamingSettings.Values["MyAccessToken"] = accessToken;
//Converting to binary format before saving since app settings cannot save in DateTime format and also we can convert it back later to the DateTime object
appRoamingSettings.Values["MyAccessTokenObtainedTime"] = accessTokenObtainedTime.ToBinary();
} else {
// Stop showing the progress bar...
mycontrols.progressbarNoShow(myprogressbar, mytextblock);
//Show the error message...
myservernetworkerror.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
}
All works fine and I can step through my code to see how it is functioning till I get into the GetRefreshedOauthStuff method. here is the GetRefreshedOauthStuff method for reference:
public async Task < string > GetRefreshedOauthStuff(string url, string MyGUID, string MyResfreshToken, string KeyName, string KeySecret, string ErrorMessage) {
//Setting up Httpclient to send to the Telligent API call.....
var client = new System.Net.Http.HttpClient();
var adminKey = String.Format("{0}:{1}", KeySecret, KeyName);
var adminKeyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(adminKey));
// Send customized headers with the token and impersonation request
client.DefaultRequestHeaders.Add("Rest-User-Token", adminKeyBase64);
client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
//Adding the long GUID to the OAUTH endpoint URL...
url = url + MyGUID + "&refresh_token=" + MyResfreshToken;
//For getting the list of posts....
System.Net.Http.HttpResponseMessage telligentResponse = await client.GetAsync(url);
if (telligentResponse.StatusCode == System.Net.HttpStatusCode.OK || telligentResponse.StatusCode == System.Net.HttpStatusCode.Forbidden) {
// Read the HTTP response content....
HttpContent responseContent = telligentResponse.Content;
// Read the response content as string.....
return await responseContent.ReadAsStringAsync();
} else {
throw new Exception("Error connecting to " + url + " ! Status: " + telligentResponse.StatusCode);
//Pop up the server or network issue message...
//mycontrols.popupMessages(ErrorMessage, "Network or Server error!");
////Navigate back to the main page....
//this.rootFrame.Navigate(typeof(MainPage));
//return null;
}
}
As soon as my code executes this line:
System.Net.Http.HttpResponseMessage telligentResponse = await client.GetAsync(url);
it steps out of this method instead of fully completing it and goes to this line in my starting method ShowInitialPosts:
string telligentResult = await forum_Hepler.MyForumPostsOauth(forumUrl, accesstoken, errorMessage);
This obviously creates problems and fails my logic. what am I doing wrong here? How do I fix this?
Any help or pointers are much appreciated
Thanks
That's exactly what await is supposed to do. It doesn't block the thread from executing - it tells the method to return control to whoever called it and then resume execution when the async method has finished.
Eric Lippert has a great blog post about this.
http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await.aspx
Related
I'm trying to test the Computer Vision SDK for .NET.
For that i've created a Computer vision resource in azure.
I create a project in .NET6 and follow the Microsoft guide to implement the api call.
But when i reach the code line:
var textHeaders = await client.ReadAsync(urlFile);
i'm getting the exception:
ComputerVisionOcrErrorException: Operation returned an invalid status code 'BadRequest'
This is the complete code:
public static void ComputerVisionPOC()
{
// Add your Computer Vision subscription key and endpoint
const string subscriptionKey = "xxxxxxx";
const string endpoint = #"https://xxxx.cognitiveservices.azure.com/";
const string readTextUrlImage = "https://drive.google.com/file/d/xxxxxxxx/view?usp=sharing";
var client = Authenticate(endpoint, subscriptionKey);
// Extract text (OCR) from a URL image using the Read
ReadFileUrl(client, readTextUrlImage).Wait();
}
public static ComputerVisionClient Authenticate(string endpoint, string key)
{
var client =
new ComputerVisionClient(new ApiKeyServiceClientCredentials(key))
{ Endpoint = endpoint };
return client;
}
public static async Task ReadFileUrl(ComputerVisionClient client, string urlFile)
{
try
{
var textHeaders = await client.ReadAsync(urlFile);
// After the request, get the operation location (operation ID)
var operationLocation = textHeaders.OperationLocation;
Thread.Sleep(2000);
// Retrieve the URI where the extracted text will be stored from the Operation-Location header.
// We only need the ID and not the full URL
const int numberOfCharsInOperationId = 36;
var operationId = operationLocation[^numberOfCharsInOperationId..];
// Extract the text
ReadOperationResult results;
Console.WriteLine($"Extracting text from URL file {Path.GetFileName(urlFile)}...");
Console.WriteLine();
do
{
results = await client.GetReadResultAsync(Guid.Parse(operationId));
} while ((results.Status == OperationStatusCodes.Running ||
results.Status == OperationStatusCodes.NotStarted));
// Display the found text.
Console.WriteLine();
var textUrlFileResults = results.AnalyzeResult.ReadResults;
foreach (var page in textUrlFileResults)
{
foreach (var line in page.Lines)
{
Console.WriteLine(line.Text);
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
I will really apreciate any help on this!
A Bad request error(400) occurs when a request which has been sent to the website server is incorrect/mistyped or corrupt and if the server receiving the request fails to understand it.
There is a possibility that the URL which has been used in the code could be wrong/mistyped.
Re-check the URL in readTextUrlImage and provide the valid one.
const string readTextUrlImage = "https://drive.google.com/file/d/xxxxxxxx/view?usp=sharing";
I have reproduced the same code with valid URL and got the expected result.
Output:
below is the code that I do pagination in Azure Cosmos. In that function I return the ContinuationToken of the FeedResponse. The first request to get the first page is fine and it return the Continuation Token. However if I used that token in the next request then the API return error 500.
I also notice that the ContinuationToken return from FeedRespone seem like in Json format like that. I have tried to get the token section only, or even copy the whole json but no cigar though
"nextToken": "[{"token":"+RID:~UVURALkfIb4FAAAAAAAAAA==#RT:1#TRC:3#RTD:hCgamV5sp6dv/pVR3z0oBTMxMzIuMTQuNDFVMTY7MjY7NDIvOTk3MzIxMlsA#ISV:2#IEO:65567#QCF:1#FPC:AQEAAAAAAAAACAAAAAAAAAA=","range":{"min":"","max":"FF"}}]"
Response from the First Page with Token return
Enter Return Token to next request and error 500
Function Code
public virtual async Task<(IEnumerable<TDomain>, string token)> ListAsync(List<ISpecification<TEntity>> specifications, PageOptions pageOptions, CancellationToken cancellationToken)
{
var container = await GetContainer(cancellationToken);
string token = null;
var result = new List<TDomain>();
QueryRequestOptions options = new QueryRequestOptions()
{
MaxItemCount = pageOptions.MaxResults
};
options.MaxItemCount = pageOptions.MaxResults;
try
{
var query = container
.GetItemLinqQueryable<TEntity>(false, pageOptions.NextToken, options)
.Specify(specifications);
var iterator = _cosmosLinqQuery.GetFeedIterator(query);
var response = await iterator.ReadNextAsync(cancellationToken);
token = response.ContinuationToken; // return a token
foreach (var item in response)
{
var mapped = _mapper.ToDomain(item);
result.Add(mapped);
}
}
catch (Exception ex)
{
var exception = new DataAccessException("Unexpected error while listing items", ex);
exception.Data["ContainerName"] = ContainerName;
throw exception;
}
return (result,token);
}
Your second screenshot is showing that you are passing a token that starts with +RID... which is not how the previous token starts (previous token starts with [{"token").
Could you be dropping the JSON wrapping attributes that are part of the token?
The second call should be passing exactly [{"token":"+RID:~UVURALkfIb4FAAAAAAAAAA==#RT:1#TRC:3#RTD:hCgamV5sp6dv/pVR3z0oBTMxMzIuMTQuNDFVMTY7MjY7NDIvOTk3MzIxMlsA#ISV:2#IEO:65567#QCF:1#FPC:AQEAAAAAAAAACAAAAAAAAAA=","range":{"min":"","max":"FF"}}].
Keep in mind that you are also sending it in the URL, so there might be character escaping there too.
I have an azure function app that I call from a slack slash command.
Sometimes the function takes a little while to return the data requested, so I made that function return a "Calculating..." message to slack immediately, and run the actual processing on a Task.Run (the request contains a webhook that I post back to when I finally get the data) :
Task.Run(() => opsData.GenerateQuoteCheckMessage(incomingData, context.FunctionAppDirectory, log));
This works mostly fine, except every now and then when people are calling the function from slack, it will return the data twice. So it will show one "Calculating..." message and then 2 results returned from the above function.
BTW, Azure functions start with :
public static async Task
Thanks!
UPDATE : here is the code for the function:
[FunctionName("QuoteCheck")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequestMessage req, TraceWriter log, ExecutionContext context)
{
var opsHelper = new OpsHelper();
string bodyContent = await req.Content.ReadAsStringAsync();
var parsedBody = HttpUtility.ParseQueryString(bodyContent);
var commandName = parsedBody["command"];
var incomingBrandId = parsedBody["text"];
int.TryParse(incomingBrandId, out var brandId);
var responseUrl = parsedBody["response_url"];
var incomingData = new IncomingSlackRequestModel
{
UserName = parsedBody["user_name"],
ChannelName = parsedBody["channel_name"],
CommandName = commandName,
ResponseUri = new Uri(responseUrl),
BrandId = brandId
};
var opsData = OpsDataFactory.GetOpsData(context.FunctionAppDirectory, environment);
Task.Run(() => opsData.GenerateQuoteCheckMessage(incomingData, context.FunctionAppDirectory, log));
// Generate a "Calculating" response message based on the correct parameters being passed
var calculatingMessage = opsHelper.GenerateCalculatingMessage(incomingData);
// Return calculating message
return req.CreateResponse(HttpStatusCode.OK, calculatingMessage, JsonMediaTypeFormatter.DefaultMediaType);
}
}
And then the GenerateQuoteCheckMessage calculates some data and eventually posts back to slack (Using Rest Sharp) :
var client = new RestClient(responseUri);
var request = new RestRequest(Method.POST);
request.AddParameter("application/json; charset=utf-8", JsonConvert.SerializeObject(outgoingMessage), ParameterType.RequestBody);
client.Execute(request);
Using Kzrystof's suggestion, I added a service bus call in the function that posts to a queue, and added another function that reads off that queue and processes the request, responding to the webhook that slack gives me :
public void DeferProcessingToServiceBus(IncomingSlackRequestModel incomingSlackRequestModel)
{
var serializedModel = JsonConvert.SerializeObject(incomingSlackRequestModel);
var sbConnectionString = ConfigurationManager.AppSettings.Get("SERVICE_BUS_CONNECTION_STRING");
var sbQueueName = ConfigurationManager.AppSettings.Get("OpsNotificationsQueueName");
var client = QueueClient.CreateFromConnectionString(sbConnectionString, sbQueueName);
var brokeredMessage = new BrokeredMessage(serializedModel);
client.Send(brokeredMessage);
}
i am facing this error, right now in oauthusing linq to twitter library:
<?xml version="1.0" encoding="UTF-8"?>
<hash>
<error>Required oauth_verifier parameter not provided</error>
<request>/oauth/access_token</request>
</hash>
i have followed this documentation link: https://linqtotwitter.codeplex.com/wikipage?title=Implementing%20OAuth%20for%20ASP.NET%20Web%20Forms&referringTitle=Learning%20to%20use%20OAuth
to implement the OAuth process, I get the this error at following line:
await auth.CompleteAuthorizeAsync(new Uri(twitterCallbackUrl));
below is the full code, please help me on this:
AspNetAuthorizer auth;
string twitterCallbackUrl = "http://127.0.0.1:58192/Default.aspx";
protected async void Page_Load(object sender, EventArgs e)
{
auth = new AspNetAuthorizer
{
CredentialStore = new SessionStateCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
},
GoToTwitterAuthorization =
twitterUrl => Response.Redirect(twitterUrl, false)
};
if (!Page.IsPostBack && Request.QueryString["oauth_token"] != null)
{
__await auth.CompleteAuthorizeAsync(new Uri(twitterCallbackUrl));__
// This is how you access credentials after authorization.
// The oauthToken and oauthTokenSecret do not expire.
// You can use the userID to associate the credentials with the user.
// You can save credentials any way you want - database, isolated
// storage, etc. - it's up to you.
// You can retrieve and load all 4 credentials on subsequent queries
// to avoid the need to re-authorize.
// When you've loaded all 4 credentials, LINQ to Twitter will let you
// make queries without re-authorizing.
//
var credentials = auth.CredentialStore;
string oauthToken = credentials.OAuthToken;
string oauthTokenSecret = credentials.OAuthTokenSecret;
string screenName = credentials.ScreenName;
ulong userID = credentials.UserID;
//Response.Redirect("~/Default.aspx", false);
}
}
protected async void AuthorizeButton_Click(object sender, EventArgs e)
{
await auth.BeginAuthorizeAsync(new Uri(twitterCallbackUrl));
//await auth.BeginAuthorizeAsync(Request.Url);
}
The problem occurs because your custom URL doesn't include the parameters that Twitter returned after the application requested authorization. If you set a breakpoint on CompleteAuthorizationAsync and type Request.Url into the Immediate window, you'll see these parameters:
If you still want to manually specify your URL, you need to include these parameters. Here's one way to do that:
string completeOAuthUrl = twitterCallbackUrl + Request.Url.Query;
await auth.CompleteAuthorizeAsync(completeOAuthUrl);
Alternatively, you can just use the page URL because that will already contains the proper parameters:
await auth.CompleteAuthorizeAsync(Request.Url);
I am using ACS to authenticate in a Windows 8 application. I am observing exactly what I expect in that the UI displays the authentication dialog and on successfully entering my LiveID credentials I am returned to my code with a Success status but I do not receive a security token, I simply get "https://XXXXX.accesscontrol.windows.net/v2/wsfederation?wa=wsignin1.0" in result.ResponseData
The code is as follows:
string loginUriString = "https://XXXXX.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=http%2f%YYYYY.cloudapp.net";
string redirectUriSting = "https://XXXXX.accesscontrol.windows.net:443/v2/wsfederation";
string authToken;
bool IsAuthenticated = false;
private async Task AuthenticateAsync()
{
var requestUri = new Uri(loginUriString, UriKind.RelativeOrAbsolute);
var redirectUri = new Uri(redirectUriSting, UriKind.RelativeOrAbsolute);
//var testUri = WebAuthenticationBroker.GetCurrentApplicationCallbackUri();
var result = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
requestUri,
redirectUri);
if (result.ResponseStatus != WebAuthenticationStatus.Success)
throw new Exception(string.Format("Login failed : {0}", result.ResponseErrorDetail));
//authToken = ExtractTokenFromResponse(result.ResponseData);
//if (!string.IsNullOrEmpty(authToken))
//{
_client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("OAuth", result.ResponseData);
IsAuthenticated = true;
//}
}
I have seen one other SO question here with what seems like a similar problem but nothing else. Have I got something wrong?
The WebAuthenticationBroker simply keeps browsing until the next requested page is the one specified by the callbackUri parameter. At that point it returns the final URL to you so if you want to get anything back it needs to be encoded in that URL.
In the ACS control panel for the relying party you need to specify a return url that is somewhere on your site. For example https://YYYYY.cloudapp.net/federationcallback. Then create a controller to handle accept a post to that URL. The post will have a form field wresult which is some xml that will contain the token returned from ACS.
You can then send the token back to the WebAuthenticationBroker by redirecting to https://YYYYY.cloudapp.net/federationcallback/end?token={whatever you want to return}
You would then need to change the usage of the authentication broker to the following:
var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
new Uri("https://XXXXX.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=http%3a%2f%2fYYYYY.cloudapp.net%2f"),
new Uri("https://YYYYY.cloudapp.net/federationcallback/end")
);
// The data you returned
var token = authenticateResult.ResponseData.Substring(authenticateResult.ResponseData.IndexOf("token=", StringComparison.Ordinal) + 6);