WepApi TaskCanceledException A task was canceled. httpClient - asp.net-mvc

It was working good, but I made some changes in the api, adding more controllers nothing out of the place, and then it stops working, always thrown an exception: "TaskCanceledException: A task was canceled" in the line GetAsync().result. I increase the timeout and infinitely stays loading.
The code controller APP who make a request to the controller API:
public ActionResult Login(LoginM us)
{
try
{
cuentaM account = new cuentaM();
HttpClient client = new HttpClient();
var result = client.GetAsync("http://localhost:26723/api/Login" + "?email=" + us.email + "&password=" + us.password).Result;
if (result.IsSuccessStatusCode)
{
account = result.Content.ReadAsAsync<cuentaM>().Result;
}
Session["cuenta"] = account;
return RedirectToAction("Index", "Home");
}
catch (Exception ex)
{
throw;
}
}
The controller API code:
public HttpResponseMessage Get(string email, string password)
{
try
{
using (elevationbEntities db = new elevationbEntities())
{
usuario user = db.usuarios.Where(m => m.email == email && m.password == password).SingleOrDefault();
cuentaM account = new cuentaM();
if (user != null)
{
account = (from o in db.cuentas
join cu in db.cuentausuarios on o.idCuenta equals cu.idCuenta
join u in db.usuarios on cu.idUsuario equals u.idUsuario
where u.idUsuario == user.idUsuario
select new cuentaM { idUsuario = user.idUsuario, idCuenta = o.idCuenta, CodigoUnico = o.CodigoUnico })
.FirstOrDefault();
}
else
{
account.Error = "Wrong Password or Email";
}
HttpResponseMessage response;
response = Request.CreateResponse(HttpStatusCode.OK, account);
return response;
}
}
catch (TaskCanceledException ex)
{
HttpResponseMessage response;
response = Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
return response;
}
}

Making blocking calls (.Result) on HttpClinet's async API can cause deadlocks especially if being used asp.net MVC, which may have async operations being invoked when the blocking call was made.
Make the code async all the way through.
Also try to avoid creating an instance of HttpClient on every request. This can cause sockets to be exhausted.
private static HttpClient client = new HttpClient();
public async Task<ActionResult> Login(LoginM us) {
try {
cuentaM account = new cuentaM();
var url = "http://localhost:26723/api/Login" + "?email=" + us.email + "&password=" + us.password
var result = await client.GetAsync(url);
if (result.IsSuccessStatusCode) {
account = await result.Content.ReadAsAsync<cuentaM>();
}
Session["cuenta"] = account;
return RedirectToAction("Index", "Home");
} catch (Exception ex) {
throw;
}
}

You may be deadlocking by blocking on an async call, as described in this article. Here's the problematic line:
account = result.Content.ReadAsAsync<cuentaM>().Result;
Change the method signature for Login to:
public async Task<ActionResult> Login(LoginM us)
Then change the problematic line to use await instead of .Result:
account = await result.Content.ReadAsAsync<cuentaM>();

Related

Xamarin iOS Oidc downloads webpage after login

I have an app that uses Oidc to login. This works fine in most cases. But there is one iphone 8+ running iOS 15.6.1 that keeps downloading the return url from the login request.
i use Xamarin forms v 5.0.0.2515, Xamarin.Essentials 1.7.3, Xamarin.CommunityToolkit 2.0.5
IdentityModel.OidcClient 3.1.2
The problem is that it works on all other devices and i can't replicate it in a dev environment.
public class WebauthenticatorBrowser : IBrowser
{
private readonly string callbackUrl;
public WebauthenticatorBrowser(string? callbackUrl = null)
{
this.callbackUrl = callbackUrl;
}
public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
{
try
{
var callBack = string.IsNullOrWhiteSpace(callbackUrl) ? options.EndUrl : callbackUrl;
var authResult = await WebAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions
{
Url = new Uri(options.StartUrl),
CallbackUrl = new Uri(callBack),
PrefersEphemeralWebBrowserSession = false
});
var authorizeResponse = ToRawIdentityUrl(options.EndUrl, authResult);
return new BrowserResult()
{
Response = authorizeResponse
};
}
catch (Exception ex)
{
return new BrowserResult()
{
Error = ex.Message
};
}
}
private string ToRawIdentityUrl(string redirectUrl, WebAuthenticatorResult result)
{
IEnumerable<string> parameters = result.Properties.Select(x => $"{x.Key}={x.Value}");
var values = string.Join("&", parameters);
return $"{redirectUrl}#{values}" ;
}
}
In my oidc class where i create my client
private async Task createOidcClient() {
document = await httpClient.GetDiscoveryDocumentAsync(idsUrl);
if (document.IsError) {
alertPresenter.Alert("Connection issue", "please try again");
return;
}
var options = new OidcClientOptions {
Authority = document.Issuer,
ClientId = "---clientID-",
Scope = "-- all the scopes --",
RedirectUri = "-- callback url--",
PostLogoutRedirectUri = " -- callback url--",
Browser = new WebauthenticatorBrowser(),
ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect
};
client = new OidcClient(options);
}
the login call in the OicdClass
public async Task<LoginResult> Login() {
await createOidcClient();
var result = await client.LoginAsync(new LoginRequest());
if (result.IsError) return result;
setAuthHeader(result.AccessToken);
await accessTokenUpdater.SetAllTokens(result.AccessToken, result.RefreshToken, result.IdentityToken);
return result;
}

How to get Microsoft 365 Planner 'All Plans for all users' using Microsoft Graph

We have got a requirement to list all Plans for all users along with their respective buckets within the organization.
I tried OAuth authorization services (AAD > App registrations) to access the planner APIs/resources using the Graph API Endpoint however I am getting access denied - It is important to mention here that the app has got all the privileges.
Does Microsoft allow to read all Planners data? provided the account has all the required permissions - if so then what am I missing here?
public static string GetAccessToken_Delegate()
{
try
{
AuthenticationContext authContext = new AuthenticationContext(Globals.AuthorityUrl, true);
AuthenticationResult authResult = authContext.AcquireTokenAsync(Globals.GraphResourceUrl, Globals.AppId, new Uri(Globals.RedirectUri), new PlatformParameters(PromptBehavior.Auto)).Result;
return authResult.AccessToken;
}
catch (Exception ex)
{
WriteLogEvents.WriteException(ex);
}
return null;
}
public static GraphServiceClient GetGraphClient(string graphToken)
{
try
{
DelegateAuthenticationProvider authenticationProvider = new DelegateAuthenticationProvider(
(requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", graphToken);
return Task.FromResult(0);
});
return new GraphServiceClient(authenticationProvider);
}
catch (Exception ex)
{
WriteLogEvents.WriteException(ex);
}
return null;
}
public async static Task<List<PlansData>> GetPlans()
{
List<PlansData> plansData = new List<PlansData>();
try
{
String accessToken = GetAccessToken_Delegate();
GraphServiceClient graphClient = GetGraphClient(accessToken);
if (graphClient != null)
{
message += string.Format("GraphClient Base Url: {0}", graphClient.BaseUrl) + Environment.NewLine;
plansData = await GetGroup(graphClient, plansData);
}
else
throw new Exception("GraphServiceClient is null");
}
catch (Exception ex)
{
WriteLogEvents.WriteException(ex);
}
finally
{
}
return plansData;
}
public async static Task<List<PlansData>> GetGroup(GraphServiceClient graphClient, List<PlansData> plansData)
{
try
{
IGraphServiceGroupsCollectionPage groupCollection = await graphClient.Groups.Request().GetAsync();
if (groupCollection?.Count > 0)
{
message += string.Format("Group Count: {0}", groupCollection.Count) + Environment.NewLine;
foreach (Microsoft.Graph.Group group in groupCollection)
{
if (group is Microsoft.Graph.Group)
{
if (group != null && group.GroupTypes != null && group.GroupTypes.Count() > 0)
{
plansData = await GetPlanData(graphClient, plansData, group);
}
}
}
}
}
catch (Exception ex)
{
WriteLogEvents.WriteException(ex);
}
return plansData;
}
public async static Task<List<PlansData>> GetPlanData(GraphServiceClient graphClient, List<PlansData> plansData, Microsoft.Graph.Group group)
{
try
{
//https://graph.microsoft.com/v1.0/groups/a047a2b3-3687-4464-bbdb-084f675c7528/planner/plans
//Get Plans Information based on the group id
IPlannerGroupPlansCollectionPage plansCollection = await graphClient.Groups[group.Id].Planner.Plans.Request().GetAsync();
if (plansCollection?.Count > 0)
{
message += string.Format("Plans Count: {0}", plansCollection.Count) + Environment.NewLine;
foreach (PlannerPlan record in plansCollection)
{
List<Bucket> bucketsList = await GetBuckets(graphClient, record.Id);
Groups groupData = new Groups(group.Id, group.DisplayName);
string siteUrl = string.Format("https://myOrg.sharepoint.com/teams/{0}/", group.MailNickname);
plansData.Add(new PlansData(record.Id, record.Title, siteUrl, groupData, bucketsList));
}
}
}
catch (Exception ex)
{
WriteLogEvents.WriteException(ex);
}
return plansData;
}
public async static Task<List<Bucket>> GetBuckets(GraphServiceClient graphClient, string planId)
{
List<Bucket> bucketsList = new List<Bucket>();
try
{
//https://graph.microsoft.com/v1.0/planner/plans/CONGZUWfGUu4msTgNP66e2UAAySi/buckets
//Get Plans Information based on the group id
IPlannerPlanBucketsCollectionPage bucketCollection = await graphClient.Planner.Plans[planId].Buckets.Request().GetAsync();
if (bucketCollection?.Count > 0)
{
message += string.Format("Buckets Count: {0}", bucketCollection.Count) + Environment.NewLine;
foreach (PlannerBucket bucket in bucketCollection)
{
Bucket b = new Bucket(bucket.Id, bucket.Name);
bucketsList.Add(b);
}
}
}
catch (Exception ex)
{
WriteLogEvents.WriteException(ex);
}
return bucketsList;
}
Thanks in advance.
You can now use application permissions to read the data.
Outdated Reply:
Planner does not support Application permissions yet. Exporting all
plan data is possible with a non-graph API and requires a delegated
access token for tenant admin. We're working on the documentation for
this and making it available from graph as well.

Not receiving access_token in three-legged oauth 2.0 flow in asp.net mvc (Blackboard Learn)

I have to implement three-legged authentication in ASP.NET MVC. I have followed the steps according to the Blackboard documentation, especially the link https://community.blackboard.com/docs/DOC-3976-three-legged-oauth
I have received authorization code by calling the REST API /learn/api/public/v1/oauth2/authorizationcode.After that according to the documentation (I followed the documentation exactly but I don't know what am I have been missing ), I built a POST request to /learn/api/public/v1/oauth2/token to get access_token but I am unable to get access_token.
Instead, access_token, I have been receiving a BadRequest. This means I am making a mistake to build my second request but I am unable to fix the problem. I haven't found any code sample in .NET to implement three legged authentication for Blackboard Learn. Could you please help me to resolve the issue?
This is my code to call both APIs to receive access_token.
public class HomeController : Controller
{
public ActionResult Index()
{
// GET /learn/api/public/v1/oauth2/authorizationcode
Guid stateId = Guid.NewGuid();
string applicationKey = "Application key goes here";
string redirectUrl = string.Format("https://Blackboard Learn URL goes here/learn/api/public/v1/oauth2/authorizationcode" +
"?redirect_uri=https://localhost:44300/Home/OAuth2Response&response_type=code&client_id={0}&scope=read&state={1}",
applicationKey, stateId);
Response.Redirect(redirectUrl, true);
return View();
}
public async Task<bool> OAuth2Response(string code = null, string state = null, string error = null, string error_description = null)
{
bool success = true;
string json = string.Empty;
string urlCommand = string.Format("/learn/api/public/v1/oauth2/token?code={0}&redirect_url=https://localhost:44300/Home/OAuth2Response", code);
try
{
using (HttpClient client = new HttpClient())
{
var endpoint = new Uri("Blackboard Learn URL goes here" + urlCommand);
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
HttpContent body = new FormUrlEncodedContent(postData);
// POST /learn/api/public/v1/oauth2/token
using (HttpResponseMessage response = await client.PostAsync(endpoint, body)) // Problem is here
{
if (response.IsSuccessStatusCode)
{
json = await response.Content.ReadAsStringAsync();
}
else
{
success = false;
}
}
}
}
catch (Exception err)
{
//hopefully we never end up here, log this exception for forensics
success = false;
}
return success;
}
}
NOTE: I can successfully receive an access_token in Postman tool.
Finally, the below code works perfectly for 3 legged authentications in ASP.NET MVC.
public class HomeController : Controller
{
//https://blackboard.jiveon.com/docs/DOC-3976-three-legged-oauth
public ActionResult Index()
{
// GET /learn/api/public/v1/oauth2/authorizationcode
Guid stateId = Guid.NewGuid();
string applicationKey = "Application key goes here";
string redirectUrl = string.Format("Blackboard Learn URL goes here/learn/api/public/v1/oauth2/authorizationcode" +
"?redirect_uri=https://localhost:44300/Home/OAuth2Response&response_type=code&client_id={0}&scope=read&state={1}",
applicationKey, stateId);
Response.Redirect(redirectUrl, true);
return View();
}
public async Task<bool> OAuth2Response(string code = null, string state = null, string error = null, string error_description = null)
{
bool success = true;
string json = string.Empty;
string urlCommand = string.Format("/learn/api/public/v1/oauth2/token?code={0}&redirect_uri=https://localhost:44300/Home/OAuth2Response", code);
try
{
using (HttpClient client = new HttpClient())
{
var endpoint = new Uri("Blackboard Learn URL goes here" + urlCommand);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes("client_id:client_secret")));
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
HttpContent body = new FormUrlEncodedContent(postData);
using (HttpResponseMessage response = await client.PostAsync(endpoint, body))
{
if (response.IsSuccessStatusCode)
{
json = await response.Content.ReadAsStringAsync();
dynamic oauth2Result = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
string access_token = oauth2Result.access_token;
string refresh_token = oauth2Result.refresh_token; }
else
{
success = false;
}
}
}
}
catch (Exception err) {
//hopefully we never end up here, log this exception for forensics
success = false;
}
return success;
}
}

Web ApI: HttpClient is not invoking my web api action

See first how i design my web api action.
[System.Web.Http.RoutePrefix("api/Appointments")]
public class AppointmentsServiceController : ApiController
{
[System.Web.Http.HttpGet, System.Web.Http.Route("UserAppointments/{email}")]
public IHttpActionResult UserAppointments(string email)
{
if (!string.IsNullOrEmpty(email))
{
AppointmentsService _appservice = new AppointmentsService();
IEnumerable<Entities.Appointments> app = _appservice.GetUserWiseAppointments(email);
if (app.Count() <= 0)
{
return NotFound();
}
else
{
return Ok(app);
}
}
else
{
return BadRequest();
}
}
}
Now this way i am calling web api action from my asp.net mvc action by HttpClient.
public async Task<ActionResult> List()
{
var fullAddress = ConfigurationManager.AppSettings["baseAddress"] + "api/Appointments/UserAppointments/" + Session["useremail"];
IEnumerable<Entities.Appointments> app = null;
try
{
using (var client = new HttpClient())
{
using (var response = client.GetAsync(fullAddress).Result)
{
if (response.IsSuccessStatusCode)
{
var customerJsonString = await response.Content.ReadAsStringAsync();
app = JsonConvert.DeserializeObject<IEnumerable<Entities.Appointments>>(customerJsonString);
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(response.Content.ReadAsStringAsync().Result);
//MessageBox.Show(dict["Message"]);
}
}
}
}
catch (HttpRequestException ex)
{
// catch any exception here
}
return View();
}
}
i want capture the return IEnumerable and if not data return that also i have to capture. please show me the right direction.
Where i made the mistake. thanks
Mixing async with blocking calls like .Result
var response = client.GetAsync(fullAddress).Result
And
response.Content.ReadAsStringAsync().Result
can lead to deadlocks, which is possibly why it it not hitting your API.
Refactor the code to be async all the way.
That would mean updating the using to
var response = await client.GetAsync(fullAddress)
and the reading of the content in the else statement to
await response.Content.ReadAsStringAsync()
Reference Async/Await - Best Practices in Asynchronous Programming
It looks like you are not awaiting your GetAsync call, so in the following if (response.IsSuccessStatusCode) is probably always returning false. Try calling your method like this:
using (var response = (await client.GetAsync(fullAddress)).Result)
{

An asynchronous module or handler completed while an asynchronous operation was still pending

Description :
I'm sending an email from MVC application, mail was sent successfuly when I send it to a single person but when I send mail to multiple people then getting an exception i.e. Operation has timed out.
So, to avoid this problem I'm trying use smtpclient.SendAsync(message,object) method but I'm getting an exception i.e. An asynchronous module or handler completed while an asynchronous operation was still pending.
How to resolve this problem ?
Code:
public async Task<string> SendEmail(List<string> ToEmailAddresses,string body, string emailSubject)
{
var smtp = new SmtpClient
{
DeliveryMethod = SmtpDeliveryMethod.Network,
Host = "xyz-host-name",
Port = 25,
EnableSsl = false,
Timeout = 600000
};
var fromAddress = new MailAddress(ConfigurationManager.AppSettings["MailUserName"], "Rewards and Recognition Team");
using (var message = new MailMessage() { Subject = emailSubject, Body = body })
{
message.From = fromAddress;
foreach (string email in ToEmailAddresses)
{
message.To.Add(email);
}
message.IsBodyHtml = true;
try
{
_logger.Log("EmailService-SendEmail-try");
object userToken = message;
smtp.SendAsync(message,userToken);
return "Success";
}
catch (Exception ex)
{
_logger.Log("EmailService-SendEmail-" + ex.Message);
return "Error";
}
}
}
you have to use the await keyword in actionresult (controller side)
like
public async task<ActionResult> SendMail(object obj)
{
var result = await SendEmail(ToEmailAddresses,body,emailSubject)
return result
}

Resources