Power Bi: Embedding Report into MVC Site - c#

I have created a power bi report. I want to embed this report to my MVC site. Here is my code:-
private static readonly string ClientID = ConfigurationManager.AppSettings["ClientID"];
private static readonly string ClientSecret = ConfigurationManager.AppSettings["ClientSecret"];
private static readonly string RedirectUrl = ConfigurationManager.AppSettings["RedirectUrl"];
private static readonly string AADAuthorityUri = ConfigurationManager.AppSettings["AADAuthorityUri"];
private static readonly string PowerBiAPI = ConfigurationManager.AppSettings["PowerBiAPI"];
private static readonly string PowerBiDataset = ConfigurationManager.AppSettings["PowerBiDataset"];
private static readonly string baseUri = PowerBiDataset;
private static string accessToken = string.Empty;
public class PBIReports
{
public PBIReport[] value { get; set; }
}
public class PBIReport
{
public string id { get; set; }
public string name { get; set; }
public string webUrl { get; set; }
public string embedUrl { get; set; }
}
public ReportController()
{
try
{
if (Request.Params.Get("code") != null)
{
Session["AccessToken"] = GetAccessToken(
HttpContext.Request["code"],
ClientID,
ClientSecret,
RedirectUrl);
}
if (Session["AccessToken"] != null)
{
accessToken = Session["AccessToken"].ToString();
GetReport(0);
}
}
catch(Exception ex)
{
}
}
public string GetAccessToken(string authorizationCode, string clientID, string clientSecret, string redirectUri)
{
TokenCache TC = new TokenCache();
string authority = AADAuthorityUri;
AuthenticationContext AC = new AuthenticationContext(authority, TC);
ClientCredential cc = new ClientCredential(clientID, clientSecret);
return AC.AcquireTokenByAuthorizationCodeAsync(
authorizationCode,
new Uri(redirectUri), cc).Result.AccessToken;
}
protected void GetReport(int index)
{
System.Net.WebRequest request = System.Net.WebRequest.Create(
String.Format("{0}/Reports",
baseUri)) as System.Net.HttpWebRequest;
request.Method = "GET";
request.ContentLength = 0;
request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken));
using (var response = request.GetResponse() as System.Net.HttpWebResponse)
{
using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
{
PBIReports Reports = JsonConvert.DeserializeObject<PBIReports>(reader.ReadToEnd());
if (Reports.value.Length > 0)
{
var report = Reports.value[index];
ViewBag["ReportId"] = report.id;
ViewBag["EmbedUrl"] = report.embedUrl;
ViewBag["ReportName"] = report.name;
ViewBag["WebUrl"] = report.webUrl;
}
}
}
}
public void GetAuthorizationCode()
{
var #params = new NameValueCollection
{
{"response_type", "code"},
{"client_id", ClientID},
{"resource", PowerBiAPI},
{ "redirect_uri", RedirectUrl}
};
var queryString = HttpUtility.ParseQueryString(string.Empty);
queryString.Add(#params);
Response.Redirect(String.Format(AADAuthorityUri + "?{0}", queryString));
}
public ActionResult Index()
{
GetAuthorizationCode();
return View();
}
On Redirecting to "Report Page", it goes for power bi login page and after I sign in it redirects back to this page (as homepage and redirect url are same). But Request.Params.Get("code") != null is null. HttpContext.Request is also null. Why? Is there anything wrong in the code?

In the constructor of a controller you don't have the this.HttpContext object to your disposal yet. You might have HttpContext.Current, but I am not sure of that.
What you should do is move that code to an action (for example your GetReport action), where you can make your checks.

Related

ASP.NET Core - How to check if a field exists from a given third party API before sending Email

In my ASP.NET Core-6 Web API, I am given a third party API to consume and then return the account details. I am using HttpClient.
api:
https://api.thirdpartycompany.com:2233/UserAccount/api/AccountDetail?accountNumber=
In appsettings.json I have:
"Endpoints": {
"customerUrl": "https://api.thirdpartycompany.com:2233/UserAccount/api/AccountDetail?accountNumber="
}
DTO:
public class GetCustomerDetailDto
{
public class CustomerDetail
{
public string AccountNumber { get; set; }
public string Fullname { get; set; }
public string EmailAddress { get; set; }
}
}
Then I have this Data Util:
public class DataUtil : IDataUtil
{
private readonly IConfiguration _config;
private readonly ILogger<DataUtil> _logger;
private readonly HttpClient _myClient;
public DataUtil
(
IConfiguration config,
ILogger<DataUtil> logger,
HttpClient myClient
)
{
_config = config;
_logger = logger;
_myClient = myClient;
}
public CustomerDetail GetCustomerDetail(string accountNumber)
{
var responseResults = new CustomerDetail();
try
{
string custAccountNoUrl = _config.GetSection("Endpoints").GetValue<string>("customerUrl") + accountNumber;
_myClient.DefaultRequestHeaders.Accept.Clear();
_myClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = _myClient.GetAsync(custAccountNoUrl).Result;
if (response.IsSuccessStatusCode)
{
var stringResult = response.Content.ReadAsStringAsync().Result;
responseResults = JsonConvert.DeserializeObject<CustomerDetail>(stringResult);
}
}
catch (Exception ex)
{
_logger.LogError($"An Error occured " + ex.ToString());
}
return responseResults;
}
}
Then this is the implementation:
public async Task<Response<string>> CreateCustomerDetailAsync(CreateDto requestDto)
{
var response = new Response<string>();
var accDetail = _dataAccess.GetCustomerDetail(requestDto.DrAccountNumber);
if (accDetail.EmailAddress != null)
{
var accountName = accDetail.Fullname;
var emailAddress = accDetail.EmailAddress.ToLower();
var mailBody = await EmailBodyBuilder.GetCustomerEmailBody(accountName, emailTempPath: "wwwroot/files/Html/CustomerEmail.html");
var mailRequest = new MailRequest()
{
Subject = "Customer Notification",
Body = mailBody,
ToEmail = emailAddress
};
bool emailResult = await _mailService.SendEmailAsync(mailRequest);
if (emailResult)
{
response.StatusCode = (int)HttpStatusCode.OK;
response.Successful = true;
response.Message = "Successful!";
return response;
}
}
}
From the third-party API given, there are moments when the customer does not have email address, then the EmailAddress field will not even appear at all from the response.
So that made me to get error here:
bool emailResult = await _mailService.SendEmailAsync(mailRequest);
I tried
if (accDetail.EmailAddress != null)
but it's not solving the problem.
Making use of
public async Task<Response<string>> CreateCustomerDetailAsync(CreateDto requestDto)
How do I make the application not to send email at all whenever EmailAddress field does not exist or it's null?

How to implement AppCenter Push API?

IMPORTANT - Microsoft is retiring AppCenter Push pretty soon, you can still follow my answer below to implement it. Or you can follow my new post at How to implement Push Notification in Xamarin with Firebase and Apple Push Notification with C# backend on using Firebase & Apple Push Notification. Thank you.
I have been reading https://learn.microsoft.com/en-us/appcenter/push/rest-api and looking over the Internet for example of how to implement this easily but found nothing useful.
I read and implemented this
https://www.andrewhoefling.com/Blog/Post/push-notifications-with-app-center-api-integration. His solution offers very good start, but incomplete.
So I enhanced Andrew Hoefling's solution from above to a full working version and thought it's good to share with fellows Xamarin members in the answer below.
public class AppCenterPush
{
User receiver = new User();
public AppCenterPush(Dictionary<Guid, string> dicInstallIdPlatform)
{
//Simply get all the Install IDs for the receipient with the platform name as the value
foreach(Guid key in dicInstallIdPlatform.Keys)
{
switch(dicInstallIdPlatform[key])
{
case "Android":
receiver.AndroidDevices.Add(key.ToString());
break;
case "iOS":
receiver.IOSDevices.Add(key.ToString());
break;
}
}
}
public class Constants
{
public const string Url = "https://api.appcenter.ms/v0.1/apps";
public const string ApiKeyName = "X-API-Token";
//Push required to use this. Go to https://learn.microsoft.com/en-us/appcenter/api-docs/index for instruction
public const string FullAccessToken = "{FULL ACCESS TOKEN}";
public const string DeviceTarget = "devices_target";
public class Apis { public const string Notification = "push/notifications"; }
//You can find your AppName and Organization/User name at your AppCenter URL as such https://appcenter.ms/users/{owner-name}/apps/{app-name}
public const string AppNameAndroid = "{APPNAME_ANDROID}";
public const string AppNameIOS = "{APPNAME_IOS}";
public const string Organization = "{ORG_OR_USER}";
}
[JsonObject]
public class Push
{
[JsonProperty("notification_target")]
public Target Target { get; set; }
[JsonProperty("notification_content")]
public Content Content { get; set; }
}
[JsonObject]
public class Content
{
public Content()
{
Name = "default"; //By default cannot be empty, must have at least 3 characters
}
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("body")]
public string Body { get; set; }
[JsonProperty("custom_data")]
public IDictionary<string, string> CustomData { get; set; }
}
[JsonObject]
public class Target
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("devices")]
public IEnumerable Devices { get; set; }
}
public class User
{
public User()
{
IOSDevices = new List<string>();
AndroidDevices = new List<string>();
}
public List<string> IOSDevices { get; set; }
public List<string> AndroidDevices { get; set; }
}
public async Task<bool> Notify(string title, string message, Dictionary<string, string> customData = default(Dictionary<string, string>))
{
try
{
//title, message length cannot exceed 100 char
if (title.Length > 100)
title = title.Substring(0, 95) + "...";
if (message.Length > 100)
message = message.Substring(0, 95) + "...";
if (!receiver.IOSDevices.Any() && !receiver.AndroidDevices.Any())
return false; //No devices to send
//To make sure in Android, title and message is retain when click from notification. Else it's lost when app is in background
if (customData == null)
customData = new Dictionary<string, string>();
if (!customData.ContainsKey("Title"))
customData.Add("Title", title);
if (!customData.ContainsKey("Message"))
customData.Add("Message", message);
//custom data cannot exceed 100 char
foreach (string key in customData.Keys)
{
if(customData[key].Length > 100)
{
customData[key] = customData[key].Substring(0, 95) + "...";
}
}
var push = new Push
{
Content = new Content
{
Title = title,
Body = message,
CustomData = customData
},
Target = new Target
{
Type = Constants.DeviceTarget
}
};
HttpClient httpClient = new HttpClient();
//Set the content header to json and inject the token
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add(Constants.ApiKeyName, Constants.FullAccessToken);
//Needed to solve SSL/TLS issue when
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
if (receiver.IOSDevices.Any())
{
push.Target.Devices = receiver.IOSDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameiOS}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
if (receiver.AndroidDevices.Any())
{
push.Target.Devices = receiver.AndroidDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameAndroid}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
return true;
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
}
}
To use this, simply do the following from your program
var receiptInstallID = new Dictionary<string, string>
{
{ "XXX-XXX-XXX-XXX", "Android" },
{ "YYY-YYY-YYY-YYY", "iOS" }
};
AppCenterPush appCenterPush = new AppCenterPush(receiptInstallID);
await appCenterPush.Notify("{YOUR_TITLE}", "{YOUR_MESSAGE}", null);
Can't add comment yet, but theres a typo in the above answer
string URL = $"{Constants.Url}/{Constants.Organization}/Constants.AppNameiOS}/{Constants.Apis.Notification}";
Should be
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameIOS}/{Constants.Apis.Notification}";
Missing { and IOS constant is capitalized.
Also, in your example to call it, Should be constructed as < guid, string >
var receiptInstallID = new Dictionary<Guid, string>
also needed as just FYI:
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
If you want to send notification for target user (user_ids_target),
public class AppCenterPush
{
User receiver = new User();
public AppCenterPush(Dictionary<string, string> dicInstallIdPlatform)
{
//Simply get all the Install IDs for the receipient with the platform name as the value
foreach (string key in dicInstallIdPlatform.Keys)
{
switch (dicInstallIdPlatform[key])
{
case "Android":
receiver.AndroidDevices.Add(key.ToString());
break;
case "iOS":
receiver.IOSDevices.Add(key.ToString());
break;
}
}
}
public class Constants
{
public const string Url = "https://api.appcenter.ms/v0.1/apps";
public const string ApiKeyName = "X-API-Token";
//Push required to use this. Go to https://learn.microsoft.com/en-us/appcenter/api-docs/index for instruction
public const string FullAccessToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
public const string DeviceTarget = "devices_target";
public const string UserTarget = "user_ids_target";
public class Apis { public const string Notification = "push/notifications"; }
//You can find your AppName and Organization/User name at your AppCenter URL as such https://appcenter.ms/users/{owner-name}/apps/{app-name}
public const string AppNameAndroid = "XXXXXX";
public const string AppNameIOS = "XXXXXX";
public const string Organization = "XXXXXXX";
}
[JsonObject]
public class Push
{
[JsonProperty("notification_target")]
public Target Target { get; set; }
[JsonProperty("notification_content")]
public Content Content { get; set; }
}
[JsonObject]
public class Content
{
public Content()
{
Name = "default"; //By default cannot be empty, must have at least 3 characters
}
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("body")]
public string Body { get; set; }
[JsonProperty("custom_data")]
public IDictionary<string, string> CustomData { get; set; }
}
[JsonObject]
public class Target
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("user_ids")]
public IEnumerable Users { get; set; }
}
public class User
{
public User()
{
IOSDevices = new List<string>();
AndroidDevices = new List<string>();
}
public List<string> IOSDevices { get; set; }
public List<string> AndroidDevices { get; set; }
}
public async Task<bool> Notify(string title, string message, Dictionary<string, string> customData = default(Dictionary<string, string>))
{
try
{
//title, message length cannot exceed 100 char
if (title.Length > 100)
title = title.Substring(0, 95) + "...";
if (message.Length > 100)
message = message.Substring(0, 95) + "...";
if (!receiver.IOSDevices.Any() && !receiver.AndroidDevices.Any())
return false; //No devices to send
//To make sure in Android, title and message is retain when click from notification. Else it's lost when app is in background
if (customData == null)
customData = new Dictionary<string, string>();
if (!customData.ContainsKey("Title"))
customData.Add("Title", title);
if (!customData.ContainsKey("Message"))
customData.Add("Message", message);
//custom data cannot exceed 100 char
foreach (string key in customData.Keys)
{
if (customData[key].Length > 100)
{
customData[key] = customData[key].Substring(0, 95) + "...";
}
}
var push = new Push
{
Content = new Content
{
Title = title,
Body = message,
CustomData = customData
},
Target = new Target
{
Type = Constants.UserTarget
}
};
HttpClient httpClient = new HttpClient();
//Set the content header to json and inject the token
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add(Constants.ApiKeyName, Constants.FullAccessToken);
//Needed to solve SSL/TLS issue when
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
if (receiver.IOSDevices.Any())
{
push.Target.Users = receiver.IOSDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameIOS}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
if (receiver.AndroidDevices.Any())
{
push.Target.Users = receiver.AndroidDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameAndroid}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
}
}
After create a function for call it;
public async void PushNotification()
{
var receiptInstallID = new Dictionary<string, string>
{
{"17593989838", "Android" }
};
var customData = new Dictionary<string, string>
{
{"taskId", "1234" }
};
AppCenterPush appCenterPush = new AppCenterPush(receiptInstallID);
await appCenterPush.Notify("Hello", "How are you?", customData);
}

[UWP]launch mobile aplications in uwp C#

I found some new APIs in Windows 10 Mobile device portal that allows to run applications on user phone .
you can launch this like to see the result : http://{PhoneIP}/api/app/packagemanager/packages
and there's another API to launch applications :
api/taskmanager/app
Starts a modern app
HTTP verb: POST
Parameters
appid : PRAID of app to start, hex64 encoded
package : Full name of the app package, hex64 encoded
I have this code to run applications but doesn't work any idea ?
public class PackageInfoToRun
{
public string appid { get; set; }
public string package { get; set; }
public string PackageFamilyName { get; set; }
}
public class PhoneInstalledPackages
{
public Installedpackage[] InstalledPackages { get; set; }
}
public class Installedpackage
{
public string Name { get; set; }
public string PackageFamilyName { get; set; }
public string PackageFullName { get; set; }
public int PackageOrigin { get; set; }
public string PackageRelativeId { get; set; }
public bool IsXAP { get; set; }
}
private static string Encode(string strn)
{
var toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(strn);
string appName64 = System.Convert.ToBase64String(toEncodeAsBytes);
appName64 = appName64.Replace(" ", "20%");
return appName64;
}
public async Task<PhoneInstalledPackages> GetInstalledApps()
{
// /api/app/packagemanager/packages
HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create("http://127.0.0.1/api/app/packagemanager/packages");
string res = "";
webrequest.Method = "GET";
try
{
using (var webresponse = await webrequest.GetResponseAsync())
using (StreamReader loResponseStream = new StreamReader(webresponse.GetResponseStream()))
{
res = loResponseStream.ReadToEnd();
}
var des = JsonConvert.DeserializeObject<PhoneInstalledPackages>(res);
return des;
}
catch (Exception ex)
{
return null;
}
}
public async Task<bool> RunAppAsync(string appid, string packagename)
{
HttpResponseMessage http = new HttpResponseMessage();
string str;
try
{
var package = new PackageInfoToRun()
{
appid = Encode(appid),
package = Encode(packagename)
};
using (HttpClient client = new HttpClient())
{
var serial = JsonConvert.SerializeObject(package);
http = await client.PostAsync("http://127.0.0.1/api/taskmanager/app", new StringContent(serial, Encoding.UTF8, "application/json"));
using (HttpResponseMessage response = http)
{
str = await response.Content.ReadAsStringAsync();
string retur = str;
if (retur.Contains("true"))
{
return true;
}
return false;
}
}
}
catch (Exception ex)
{
return false;
}
}
what's going wrong in my codes ? thanks :)
I used your code with small changes and it works for me.
now RunAppAsync looks like this. Don't forget to specify a PORT.
HttpClient client = new HttpClient();
var serial = JsonConvert.SerializeObject(package);
var result = await client.PostAsync(String.Format("http://127.0.0.1:*PORT*/api/taskmanager/app?appid={0}&package={1}", package.appid, package.package), new StringContent(serial));
Where the package is
var package = new PackageInfoToRun()
{
appid = Encode(app.PackageRelativeId),
package = Encode(app.PackageFullName),
};
And the app variable is an instance of InstalledPackage class.

WebRequest 400 bad request to api

I am making webrequest to instagram api, and recieving 400 bad request, but i guess this is not something specific to instagram and is a generic error in my code.
here is my code."code" paramter in this request is obtained from previous request.Code breaks at "GetResponse" method
string clientID = "myclientid";
string clientsecret="myclientsecret";
string redirectURL = "http://localhost:64341/";
string webrequestUri = "https://api.instagram.com/oauth/
access_token? client_id=" + clientID + "&client_secret=
" + clientsecret + "&grant_type=authorization_code" + "&redirect_uri="redirectURL+"&code="
+Request.QueryString["code"].ToString();
WebRequest request = WebRequest.Create(webrequestUri);
request.Method = "POST";
WebResponse response = request.GetResponse();
C#/asp.net Working example:
namespace InstagramLogin.Code
{
public class InstagramAuth
{
private InstagramClient myApp = new InstagramClient();
public string genUserAutorizationUrl()
{
return String.Format(myApp.OAuthAutorizeURL, myApp.ClientId, myApp.RedirectUri);
}
public AuthUserApiToken getTokenId(string CODE)
{
//curl \-F 'client_id=CLIENT-ID' \
//-F 'client_secret=CLIENT-SECRET' \
//-F 'grant_type=authorization_code' \
//-F 'redirect_uri=YOUR-REDIRECT-URI' \
//-F 'code=CODE' \https://api.instagram.com/oauth/access_token
var wc = new WebClient();
var wcResponse = wc.UploadValues(myApp.AuthAccessTokenUrl,
new System.Collections.Specialized.NameValueCollection() {
{ "client_id", myApp.ClientId },
{ "client_secret", myApp.ClientSecret },
{ "grant_type", "authorization_code"},
{ "redirect_uri", myApp.RedirectUri},
{ "code", CODE}
});
var decodedResponse = wc.Encoding.GetString(wcResponse);
AuthUserApiToken UserApiToken = JsonConvert.DeserializeObject<AuthUserApiToken>(decodedResponse);
return UserApiToken;
}
}
}
your object:
namespace InstagramLogin.Code
{
public class InstagramClient
{
private const string ApiUriDefault = "https://api.instagram.com/v1/";
private const string OAuthUriDefault = "https://api.instagram.com/oauth/";
private const string RealTimeApiDefault = "https://api.instagram.com/v1/subscriptions/";
private const string OAuthAutorizeURLDefault = "https://api.instagram.com/oauth/authorize/?client_id={0}&redirect_uri={1}&response_type=code";
private const string AuthAccessTokenUrlDefault = "https://api.instagram.com/oauth/access_token";
private const string clientId = "clientid";
private const string clientSecretId = "clientsecretid";
private const string redirectUri = "http://localhost/InstagramLogin/InstaAuth.aspx";
private const string websiteUri = "http://localhost/InstagramLogin/InstaAuth.aspx";
public string ApiUri { get; set; }
public string OAuthUri { get; set; }
public string RealTimeApi { get; set; }
public string OAuthAutorizeURL { get { return OAuthAutorizeURLDefault; } }
public string ClientId { get { return clientId; } }
public string ClientSecret { get { return clientSecretId; } }
public string RedirectUri { get { return redirectUri; } }
public string AuthAccessTokenUrl { get { return AuthAccessTokenUrlDefault; } }
//removed props
}
}
instagram loged user:
namespace InstagramLogin.Code
{
public class SessionInstaAuthUser
{
public bool isInSession()
{
return HttpContext.Current.Session["AuthUserApiToken"] != null;
}
public AuthUserApiToken get()
{
if (isInSession())
{
return HttpContext.Current.Session["AuthUserApiToken"] as AuthUserApiToken;
}
return null;
}
public void set(AuthUserApiToken value)
{
HttpContext.Current.Session["AuthUserApiToken"] = value;
}
public void clear()
{
if (isInSession())
{
HttpContext.Current.Session["AuthUserApiToken"] = null;
}
}
}
}
markup:
<asp:Button ID="btnInstaAuth"
Text="Click here to get instagram auth"
runat="server" />
Code behind:
//objects
private InstagramClient InstagramApiCLient = new InstagramClient();
private InstagramAuth AuthManager = new InstagramAuth();
private SessionInstaAuthUser SesssionInstaUser = new SessionInstaAuthUser();
//click login with tests - user is logged (in session)
void btnInstaAuth_Click(Object sender,
EventArgs e)
{
btnGetInstaPosts.Visible = false;
if (SesssionInstaUser.isInSession())
{
SesssionInstaUser.clear();
Button clickedButton = (Button)sender;
clickedButton.Text = "Click here to get instagram auth";
btnInstaAuth.Visible = true;
}
else
{
Response.Redirect(AuthManager.genUserAutorizationUrl());
}
}
You can find what you need in this class InstagramAuth sorry if I did forgot to remove some of extra methods or properties, please strip it out.
This button can be used on all page, still don't forget to add on page load at the page set in instagram as login page, query string parse:
//here you read the token instagram generated and append it to the session, or get the error :)
if (!IsPostBack)
{
if (!SesssionInstaUser.isInSession())
{
if (Request.QueryString["code"] != null)
{
var token = AuthManager.getTokenId(Request.QueryString["code"]);
SesssionInstaUser.set(token);
//set login button to have option to log out
btnInstaAuth.Text = token.user.username + " leave instagtam oAuth";
Response.Redirect(Request.Url.ToString().Split('?')[0]);
}
else
if (Request.QueryString["error"] != null)
{
btnInstaAuth.Text = Request.QueryString["error_description"];
}
}
}
Sorry If I'd missed something, php curl in to c# is implemented in first class.
Update (I did forget something), insta user obj :)
namespace InstagramLogin.Code
{
public class UserLogged
{
public string id { get; set; }
public string username { get; set; }
public string full_name { get; set; }
public string profile_picture { get; set; }
}
public class AuthUserApiToken
{
public string access_token { get; set; }
public UserLogged user { get; set; }
}
}

WP8 HttpClient.PostAsync never returns result

I have a Windows Phone 8 app where I am calling await HttpClient.PostAsync and it never returns a result. It just sits there and hangs. If I run the exact same code from a console app, it returns the result almost immediately. All of the code doing the work resides in a portable class library. I would appreciate any help you may be able to give. All of the other issues I have found on this state to use await client.PostAsync, which I am already doing.
The code in my class library is as such:
public class Authenticator
{
private const string ApiBaseUrl = "http://api.fitbit.com";
private const string Callback = "http://myCallbackUrlHere";
private const string SignatureMethod = "HMAC-SHA1";
private const string OauthVersion = "1.0";
private const string ConsumerKey = "myConsumerKey";
private const string ConsumerSecret = "myConsumerSecret";
private const string RequestTokenUrl = "http://api.fitbit.com/oauth/request_token";
private const string AccessTokenUrl = "http://api.fitbit.com/oauth/access_token";
private const string AuthorizeUrl = "http://www.fitbit.com/oauth/authorize";
private string requestToken;
private string requestTokenSecret;
public string GetAuthUrlToken()
{
return GenerateAuthUrlToken().Result;
}
private async Task<string> GenerateAuthUrlToken()
{
var httpClient = new HttpClient { BaseAddress = new Uri(ApiBaseUrl) };
var timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0);
var oauthTimestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString(CultureInfo.InvariantCulture);
var oauthNonce = DateTime.Now.Ticks.ToString(CultureInfo.InvariantCulture);
var authHeaderValue = string.Format(
"oauth_callback=\"{0}\",oauth_consumer_key=\"{1}\",oauth_nonce=\"{2}\"," +
"oauth_signature=\"{3}\",oauth_signature_method=\"{4}\"," +
"oauth_timestamp=\"{5}\",oauth_version=\"{6}\"",
Uri.EscapeDataString(Callback),
Uri.EscapeDataString(ConsumerKey),
Uri.EscapeDataString(oauthNonce),
Uri.EscapeDataString(this.CreateSignature(RequestTokenUrl, oauthNonce, oauthTimestamp, Callback)),
Uri.EscapeDataString(SignatureMethod),
Uri.EscapeDataString(oauthTimestamp),
Uri.EscapeDataString(OauthVersion));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"OAuth",
authHeaderValue);
var content = new StringContent(string.Empty);
var response = await httpClient.PostAsync(RequestTokenUrl, content);
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception("Request Token Step Failed");
}
var responseContent = await response.Content.ReadAsStringAsync();
var responseItems = responseContent.Split(new[] { '&' });
this.requestToken = responseItems[0];
this.requestTokenSecret = responseItems[1];
var url = string.Format("{0}?{1}&display=touch", AuthorizeUrl, this.requestToken);
return url;
}
public string CreateSignature(string url, string nonce, string timestamp, string callback)
{
// code removed
return signatureString;
}
private static byte[] StringToAscii(string s)
{
// code removed
return retval;
}
}
I have a console app that calls this library and it works with no problem:
public class Program
{
public static void Main(string[] args)
{
var o = new Program();
o.LinkToFitbit();
}
public void LinkToFitbit()
{
var authenticator = new Authenticator();
// this works and returns the url immediately
var url = authenticator.GetAuthUrlToken();
// code removed
}
}
When I run from my WP8 app, it just hangs when it gets to this line in the library:
var response = await httpClient.PostAsync(RequestTokenUrl, content);
Here is my WP8 code:
public partial class FitbitConnector : PhoneApplicationPage
{
public FitbitConnector()
{
InitializeComponent();
this.AuthenticateUser();
}
private void AuthenticateUser()
{
var authenticator = new Authenticator();
var url = authenticator.GetAuthUrlToken();
// code removed
}
}
This line is blocking the UI thread:
public string GetAuthUrlToken()
{
return GenerateAuthUrlToken().Result;
}
The code after the await httpClient.PostAsync() needs to be executed in the UI thread, but it can't be executed because is is blocked.
So, replace this:
private void AuthenticateUser()
{
var authenticator = new Authenticator();
var url = authenticator.GetAuthUrlToken();
// code removed
}
With this:
private async void AuthenticateUser()
{
var authenticator = new Authenticator();
var url = await authenticator.GenerateAuthUrlToken();
// code removed
}
Notice I am using async and await. You will need to make GenerateAuthUrlToken() public. You can erase GetAuthUrlToken().
In few words, Task<T>.Result is not asynchronous.
Can you post CreateSignature and StringToAscii code ? I am stuck at basic oAuth.
I can get Request token but get "Invalid OAuth Signature" while performing a request for Access Token.
Nexus, here you go. I created an OauthHelper class that I use to build the pieces I need. Have a look below. I hope this helps.
public static class OauthHelper
{
public const string ConsumerKey = "MyKey";
public const string ConsumerSecret = "MySecret";
public const string UriScheme = "https";
public const string HostName = "api.somePlace.com";
public const string RequestPath = "/services/api/json/1.3.0";
public const string OauthSignatureMethod = "HMAC-SHA1";
public const string OauthVersion = "1.0";
public static string BuildRequestUri(Dictionary<string, string> requestParameters)
{
var url = GetNormalizedUrl(UriScheme, HostName, RequestPath);
var allParameters = new List<QueryParameter>(requestParameters.Select(entry => new QueryParameter(entry.Key, entry.Value)));
var normalizedParameters = NormalizeParameters(allParameters);
var requestUri = string.Format("{0}?{1}", url, normalizedParameters);
return requestUri;
}
public static AuthenticationHeaderValue CreateAuthorizationHeader(
string oauthToken,
string oauthNonce,
string oauthTimestamp,
string oauthSignature)
{
var normalizedUrl = GetNormalizedUrl(UriScheme, HostName, RequestPath);
return CreateAuthorizationHeader(oauthToken, oauthNonce, oauthTimestamp, oauthSignature, normalizedUrl);
}
public static AuthenticationHeaderValue CreateAuthorizationHeader(
string oauthToken,
string oauthNonce,
string oauthTimestamp,
string oauthSignature,
string realm)
{
if (string.IsNullOrWhiteSpace(oauthToken))
{
oauthToken = string.Empty;
}
if (string.IsNullOrWhiteSpace(oauthTimestamp))
{
throw new ArgumentNullException("oauthTimestamp");
}
if (string.IsNullOrWhiteSpace(oauthNonce))
{
throw new ArgumentNullException("oauthNonce");
}
if (string.IsNullOrWhiteSpace(oauthSignature))
{
throw new ArgumentNullException("oauthSignature");
}
var authHeaderValue = string.Format(
"realm=\"{0}\"," +
"oauth_consumer_key=\"{1}\"," +
"oauth_token=\"{2}\"," +
"oauth_nonce=\"{3}\"," +
"oauth_timestamp=\"{4}\"," +
"oauth_signature_method=\"{5}\"," +
"oauth_version=\"{6}\"," +
"oauth_signature=\"{7}\"",
realm,
Uri.EscapeDataString(ConsumerKey),
Uri.EscapeDataString(oauthToken),
Uri.EscapeDataString(oauthNonce),
Uri.EscapeDataString(oauthTimestamp),
Uri.EscapeDataString(OauthSignatureMethod),
Uri.EscapeDataString(OauthVersion),
Uri.EscapeDataString(oauthSignature));
var authHeader = new AuthenticationHeaderValue("OAuth", authHeaderValue);
return authHeader;
}
public static string CreateSignature(
string httpMethod,
string oauthToken,
string oauthTokenSecret,
string oauthTimestamp,
string oauthNonce,
Dictionary<string, string> requestParameters)
{
// get normalized url
var normalizedUrl = GetNormalizedUrl(UriScheme, HostName, RequestPath);
return CreateSignature(
httpMethod,
oauthToken,
oauthTokenSecret,
oauthTimestamp,
oauthNonce,
requestParameters,
normalizedUrl);
}
public static string CreateSignature(
string httpMethod,
string oauthToken,
string oauthTokenSecret,
string oauthTimestamp,
string oauthNonce,
Dictionary<string, string> requestParameters,
string realm)
{
if (string.IsNullOrWhiteSpace(httpMethod))
{
throw new ArgumentNullException("httpMethod");
}
if (string.IsNullOrWhiteSpace(oauthToken))
{
oauthToken = string.Empty;
}
if (string.IsNullOrWhiteSpace(oauthTokenSecret))
{
oauthTokenSecret = string.Empty;
}
if (string.IsNullOrWhiteSpace(oauthTimestamp))
{
throw new ArgumentNullException("oauthTimestamp");
}
if (string.IsNullOrWhiteSpace(oauthNonce))
{
throw new ArgumentNullException("oauthNonce");
}
var allParameters = new List<QueryParameter>
{
new QueryParameter("oauth_consumer_key", ConsumerKey),
new QueryParameter("oauth_token", oauthToken),
new QueryParameter("oauth_nonce", oauthNonce),
new QueryParameter("oauth_timestamp", oauthTimestamp),
new QueryParameter("oauth_signature_method", OauthSignatureMethod),
new QueryParameter("oauth_version", OauthVersion)
};
allParameters.AddRange(requestParameters.Select(entry => new QueryParameter(entry.Key, entry.Value)));
// sort params
allParameters.Sort(new QueryParameterComparer());
// concat all params
var normalizedRequestParameters = NormalizeParameters(allParameters);
// create base string
var signatureBase = string.Format(
"{0}&{1}&{2}",
UrlEncode(httpMethod.ToUpperInvariant()),
UrlEncode(realm),
UrlEncode(normalizedRequestParameters));
var signatureKey = string.Format(
"{0}&{1}",
UrlEncode(ConsumerSecret),
UrlEncode(oauthTokenSecret));
// hash the base string
var hmacsha1 = new HMACSHA1(StringToAscii(signatureKey));
var signatureString = Convert.ToBase64String(hmacsha1.ComputeHash(StringToAscii(signatureBase)));
return signatureString;
}
public static string GenerateNonce()
{
var ts = new TimeSpan(DateTime.Now.Ticks);
var ms = ts.TotalMilliseconds.ToString().Replace(".", string.Empty);
var nonce = ms;
return nonce;
}
public static string GenerateTimeStamp()
{
var timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0);
var timestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString(CultureInfo.InvariantCulture);
return timestamp;
}
private static string GetNormalizedUrl(string uriScheme, string hostName, string requestPath)
{
var normalizedUrl = string.Format(
"{0}://{1}{2}",
uriScheme.ToLowerInvariant(),
hostName.ToLowerInvariant(),
requestPath);
return normalizedUrl;
}
private static string NormalizeParameters(IList<QueryParameter> parameters)
{
var result = new StringBuilder();
for (var i = 0; i < parameters.Count; i++)
{
var p = parameters[i];
result.AppendFormat("{0}={1}", p.Name, p.Value);
if (i < parameters.Count - 1)
{
result.Append("&");
}
}
return result.ToString();
}
private static byte[] StringToAscii(string s)
{
var retval = new byte[s.Length];
for (var ix = 0; ix < s.Length; ++ix)
{
var ch = s[ix];
if (ch <= 0x7f)
{
retval[ix] = (byte)ch;
}
else
{
retval[ix] = (byte)'?';
}
}
return retval;
}
private static string UrlEncode(string value)
{
const string Unreserved = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
var result = new StringBuilder();
foreach (char symbol in value)
{
if (Unreserved.IndexOf(symbol) != -1)
{
result.Append(symbol);
}
else
{
result.Append('%' + string.Format("{0:X2}", (int)symbol));
}
}
return result.ToString();
}
}
public class QueryParameter
{
public QueryParameter(string name, string value)
{
this.Name = name;
this.Value = value;
}
public string Name { get; private set; }
public string Value { get; private set; }
}
public class QueryParameterComparer : IComparer<QueryParameter>
{
public int Compare(QueryParameter x, QueryParameter y)
{
return x.Name == y.Name
? string.Compare(x.Value, y.Value)
: string.Compare(x.Name, y.Name);
}
}
Since you are using .Result or .Wait or await this will end up causing a deadlock in your code.
you can use ConfigureAwait(false) in async methods for preventing deadlock
like this:
var response = await httpClient.PostAsync(RequestTokenUrl, content).ConfigureAwait(false);
you can use ConfigureAwait(false) wherever possible for Don't Block Async Code .

Categories

Resources