How to Copy Cookie to WKWebview in iOS? - cookies

I don't know how/which cookies are coming from which website. Because of that, I can't set manually the cookie names.
How can I get third party cookies to paste to a WKWebview? Here is my code but no chance.
My webview;
public class CustomWebView : WebView
{
public static readonly BindableProperty UriProperty =
BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(Uri),
declaringType: typeof(CustomWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
}
My custom renderer (Shouldn't necessary an event per request? This method fires once in the first request);
[assembly: ExportRenderer(typeof(CustomWebView), typeof(HTMobile.iOS.WebViewRenderer))]
namespace HTMobile.iOS
{
public class WebViewRenderer : ViewRenderer<CustomWebView, WKWebView>
{
protected override void OnElementChanged(ElementChangedEventArgs<CustomWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
// Cookie
var cookieUrl = new Uri("abc.com");
NSHttpCookieStorage.SharedStorage.AcceptPolicy = NSHttpCookieAcceptPolicy.Always;
var cookieJar = NSHttpCookieStorage.SharedStorage;
cookieJar.AcceptPolicy = NSHttpCookieAcceptPolicy.Always;
foreach (var aCookie in cookieJar.Cookies)
{
cookieJar.DeleteCookie(aCookie);
}
var jCookies = UserInfo.CookieContainer.GetCookies(cookieUrl);
IList<NSHttpCookie> eCookies =
(from object jCookie in jCookies
where jCookie != null
select (Cookie)jCookie
into netCookie
select new NSHttpCookie(netCookie)).ToList();
cookieJar.SetCookies(eCookies.ToArray(), cookieUrl, cookieUrl);
// WebView Instance
webView = new WKWebView(Frame, new WKWebViewConfiguration());
SetNativeControl(webView);
if (e.NewElement != null)
{
Control.LoadRequest(new NSUrlRequest(new NSUrl("abc.com")));
}
}
}
}
}
I think an event should be fired per request and I should be able to get a cookie list for the visited page and then set it to my WebView.
Advise, please.

You can have a try with getting cookie by invoking DecidePolicy method from WKNavigationDelegate .
public class NavigationDelegate : WKNavigationDelegate
{
NSMutableArray multiCookieArr = new NSMutableArray();
public override void DecidePolicy(WKWebView webView, WKNavigationResponse navigationResponse, [BlockProxy(typeof(Action))]Action<WKNavigationResponsePolicy> decisionHandler)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
{
WKHttpCookieStore wKHttpCookieStore = webView.Configuration.WebsiteDataStore.HttpCookieStore;
Console.WriteLine("wKHttpCookieStore is :" + wKHttpCookieStore.GetDebugDescription());
wKHttpCookieStore.GetAllCookies(cookies => {
if(cookies.Length > 0)
{
foreach (NSHttpCookie cookie in cookies)
{
//NSHttpCookieStorage.SharedStorage.SetCookie(cookie);
Console.WriteLine("cookie is :" + cookie);
}
}
});
}
else
{
NSHttpUrlResponse response = navigationResponse.Response as NSHttpUrlResponse;
NSHttpCookie[] cookiesAll = NSHttpCookie.CookiesWithResponseHeaderFields(response.AllHeaderFields, response.Url);
foreach (NSHttpCookie cookie in cookiesAll)
{
Console.WriteLine("Here is the cookie inside wkwebview is :" + cookie);
NSArray cookieArr = NSArray.FromObjects(cookie.Name, cookie.Value, cookie.Domain, cookie.Path);
multiCookieArr.Add(cookieArr);
}
Console.WriteLine("cookie is :" + cookiesAll);
}
decisionHandler(WKNavigationResponsePolicy.Allow);
//base.DecidePolicy(webView, navigationResponse, decisionHandler);
}
In addition , using Renderer to Customizing a WebView you can refer to this doc .
public class HybridWebViewRenderer : WkWebViewRenderer
{
public HybridWebViewRenderer() : this(new WKWebViewConfiguration())
{
}
public HybridWebViewRenderer(WKWebViewConfiguration config) : base(config)
{
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
//...
}
if (e.NewElement != null)
{
this.NavigationDelegate = new NavigationDelegat();
}
}
}

Related

Xamarin WKWebView and Cookies

I have a Xamarin Forms app that uses cookies to track login status and uses both HTTPRequests and Webviews, so both need to share cookies. With UIWebView this cookies were shared without any extra management on my part; with WKWebView this appears not to be the case. I have been searching for an explanation on how cookies are handled with WKWebView or an example of how to manually retrieve and set the cookies between these two objects, but have been unable to find any. How do I get the cookie behavior that I have relied on when using UIWebView with WKWebView?
When I tried to implement a WKNamvigationDelegate the WebView OnLoadFinished was not called, so my loading indicator remained after loading was complete. What ended up working for me is in my iOS CustomWebViewRenderer's constructor I call this function to clear out any existing cookies and copy any cookies from the HTTP Shared Storage into the webview:
protected async void SetCookies()
{
var dataStore = WKWebsiteDataStore.DefaultDataStore;
var cookies = NSHttpCookieStorage.SharedStorage.Cookies;
var oldcookies = await dataStore.HttpCookieStore.GetAllCookiesAsync();
foreach (var cookie in oldcookies)
{
await dataStore.HttpCookieStore.DeleteCookieAsync(cookie);
}
foreach (var cookie in cookies)
{
await dataStore.HttpCookieStore.SetCookieAsync(cookie);
}
}
To get the cookies from the webview I have existing in shared code a CustomWebView that uses OnShouldLoad to detect the indication of a successful login, then call platform specific code. This was created to handle Android cookies, but will now work for iOS as well. The iOS implementation clears out any existing HTTP shared storage cookies and copies the cookies from the webview into the Shared Storage.
public async Task GetCookiesFromWebview()
{
var dataStore = WKWebsiteDataStore.DefaultDataStore;
var cookies = await dataStore.HttpCookieStore.GetAllCookiesAsync();
var oldcookies = NSHttpCookieStorage.SharedStorage.Cookies;
foreach (var cookie in oldcookies)
{
NSHttpCookieStorage.SharedStorage.DeleteCookie(cookie);
}
foreach (var cookie in cookies)
{
NSHttpCookieStorage.SharedStorage.SetCookie(cookie);
}
return;
}
You can create a custom WKNavigationDelegate.
public class MyCustomWebViewDelegate : WKNavigationDelegate
{
public override void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, Action<WKNavigationActionPolicy> decisionHandler)
{
var jCookies = Appl.FlurlClient.Cookies.Values;
NSHttpCookie[] nsHttpCookies = jCookies.Where(c => c != null).Select(c => new NSHttpCookie(c)).ToArray();
foreach (var c in nsHttpCookies)
{
webView.Configuration.WebsiteDataStore.HttpCookieStore.SetCookie(c);
}
decisionHandler(WKNavigationActionPolicy.Allow);
}
}
and assign in to webView as :
webView.NavigationDelegate = new MyCustomWebViewDelegate();
There is difference above iOS 12 to get cookie from WKWebview .You can have a try with getting cookie by invoking DecidePolicy method from WKNavigationDelegate .
public class NavigationDelegate : WKNavigationDelegate
{
NSMutableArray multiCookieArr = new NSMutableArray();
public override void DecidePolicy(WKWebView webView, WKNavigationResponse navigationResponse, [BlockProxy(typeof(Action))]Action<WKNavigationResponsePolicy> decisionHandler)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
{
WKHttpCookieStore wKHttpCookieStore = webView.Configuration.WebsiteDataStore.HttpCookieStore;
Console.WriteLine("wKHttpCookieStore is :" + wKHttpCookieStore.GetDebugDescription());
wKHttpCookieStore.GetAllCookies(cookies => {
if(cookies.Length > 0)
{
foreach (NSHttpCookie cookie in cookies)
{
//NSHttpCookieStorage.SharedStorage.SetCookie(cookie);
Console.WriteLine("cookie is :" + cookie);
}
}
});
}
else
{
NSHttpUrlResponse response = navigationResponse.Response as NSHttpUrlResponse;
NSHttpCookie[] cookiesAll = NSHttpCookie.CookiesWithResponseHeaderFields(response.AllHeaderFields, response.Url);
foreach (NSHttpCookie cookie in cookiesAll)
{
Console.WriteLine("Here is the cookie inside wkwebview is :" + cookie);
NSArray cookieArr = NSArray.FromObjects(cookie.Name, cookie.Value, cookie.Domain, cookie.Path);
multiCookieArr.Add(cookieArr);
}
Console.WriteLine("cookie is :" + cookiesAll);
}
decisionHandler(WKNavigationResponsePolicy.Allow);
//base.DecidePolicy(webView, navigationResponse, decisionHandler);
}
The renderer code as follow :
public class HybridWebViewRenderer : WkWebViewRenderer
{
public HybridWebViewRenderer() : this(new WKWebViewConfiguration())
{
}
public HybridWebViewRenderer(WKWebViewConfiguration config) : base(config)
{
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
//...
}
if (e.NewElement != null)
{
this.NavigationDelegate = new NavigationDelegat();
}
}
}
Also can refer to this dicussion :How to Copy Cookie to WKWebview in iOS?

How to manage Facebook login to avoid authentication frequently in Xamarin.Forms?

I use following code for Facebook login and access user information like albums and pictures. I have set code to get access token using following code. Now, the problem is I need to get access token everytime when user open application. However, once user authenticate, application will not ask for authenticate until user close the application. But it will ask for authenticate again after user reopen application. This way user will frustrate if they will ask to authentication everytime they will try to access albums or any other things of facebook.
Is there anyway to skip this? I mean once user provided access of Facebook, application must not ask for login(authenticate). I will have access token but I don't know how to use to play with authentication. So, we can avoid authentication frequently.
My Code:
public class FacebookService : IFacebookService
{
private readonly string[] permissions = { "public_profile", "email", "user_birthday", "user_photos" };
public event EventHandler<FacebookUser> LoginCompleted;
public string Token => AccessToken.CurrentAccessToken.TokenString;
public void Logout()
{
LoginManager manager = new LoginManager();
manager.LogOut();
}
public void LogInToFacebook()
{
if (AccessToken.CurrentAccessToken == null)
{
ObtainNewToken(LogInToFacebook);
return;
}
var fields = new[] { "name", "email", "birthday", "gender", "picture" };
var query = $"/me?fields={string.Join(",", fields)}";
var token = AccessToken.CurrentAccessToken.TokenString;
var request = new GraphRequest(query, null, token, null, "GET");
request.Start((connection, result, error) =>
{
if (error != null)
{
HandleError(error.LocalizedDescription);
}
else
{
var userInfo = result as NSDictionary;
var id = userInfo["id"].ToString();
var email = userInfo["email"].ToString();
var name = userInfo["name"].ToString();
var birthday = userInfo["birthday"].ToString();
var gender = userInfo["gender"].ToString();
var picture = ((userInfo["picture"] as NSDictionary)["data"] as NSDictionary)["url"].ToString();
var args = new FacebookUser(id, email, name, birthday, gender, picture);
LoginCompleted?.Invoke(this, args);
}
});
}
public async System.Threading.Tasks.Task RequestAlbums(Action<FacebookAlbum[]> callback)
{
if (AccessToken.CurrentAccessToken == null)
{
ObtainNewTokenForAlbum(callback);
return;
}
using (HttpClient client = new HttpClient())
{
try
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
var host = "https://graph.facebook.com/";
var json = await client.GetStringAsync($"{host}me/albums");
var data = JObject.Parse(json).First.First.ToString();
var albums = JsonConvert.DeserializeObject<FacebookAlbum[]>(data);
var getPhotosTasks = new List<System.Threading.Tasks.Task>();
foreach (var album in albums)
getPhotosTasks.Add(System.Threading.Tasks.Task.Run(() => RequestPhotos(album)));
await System.Threading.Tasks.Task.WhenAll(getPhotosTasks.ToArray());
callback(albums);
}
catch (Exception ex1)
{
HandleError(ex1.Message);
}
}
}
private void ObtainNewTokenForAlbum(Action<FacebookAlbum[]> callback)
{
var login = new LoginManager();
login.LogInWithReadPermissions(permissions, null, (r, e) =>
{
if (e == null && !r.IsCancelled)
{
RequestAlbums(callback);
}
else
HandleError(e?.LocalizedDescription);
});
}
private async System.Threading.Tasks.Task RequestPhotos(FacebookAlbum album)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
try
{
var host = "https://graph.facebook.com/";
var json = await client.GetStringAsync($"{host}{album.Id}/photos?fields=source,picture");
var data = JObject.Parse(json)["data"].ToString();
album.Photos = JsonConvert.DeserializeObject<FacebookPicture[]>(data);
}
catch (Exception exc)
{
HandleError(exc.Message);
}
}
}
private void ObtainNewToken(Action callback)
{
var login = new LoginManager();
login.LogInWithReadPermissions(permissions, null, (r, e) =>
{
if (e == null && !r.IsCancelled)
callback?.Invoke();
else
HandleError(e?.LocalizedDescription);
});
}
private void HandleError(string messageDescription)
{
messageDescription = messageDescription ?? "Request was cancelled";
_notificationService.DisplayNotification(messageDescription, Colors.d8Red);
}
}
AppDelegate
public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary launchOptions)
{
UAirship.TakeOff();
RegisterServices();
SetupFacebookSDK();
FFImageLoading.Forms.Touch.CachedImageRenderer.Init();
var dummy = new FFImageLoading.Forms.Touch.CachedImageRenderer();
Xamarin.Forms.Forms.Init();
LoadApplication(new App());
UIApplication.SharedApplication.StatusBarHidden = false;
UIApplication.SharedApplication.SetStatusBarStyle(UIStatusBarStyle.LightContent, false);
_networkManager = new NetworkManager();
OverrideDefaultListViewCustomActionsColors();
UAirship.Push.UserPushNotificationsEnabled = true;
new PhotoAccessChecker();
return ApplicationDelegate.SharedInstance.FinishedLaunching(uiApplication, launchOptions);
}
void SetupFacebookSDK()
{
FacebookProfile.EnableUpdatesOnAccessTokenChange(true);
FacebookSettings.AppID = "000000000049000";
FacebookSettings.DisplayName = "MyProduct";
}
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
return ApplicationDelegate.SharedInstance.OpenUrl(application, url, sourceApplication, annotation);
}
I guess you forgot initialize FBSDK in AppDelegate.
Check your code if return ApplicationDelegate.SharedInstance.FinishedLaunching (application, launchOptions); has been executed in FinishedLaunching.
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
Settings.AppID = appId;
Settings.DisplayName = appName;
// ...
// This method verifies if you have been logged into the app before, and keep you logged in after you reopen or kill your app.
return ApplicationDelegate.SharedInstance.FinishedLaunching (application, launchOptions);
}
public override bool OpenUrl (UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
// We need to handle URLs by passing them to their own OpenUrl in order to make the SSO authentication works.
return ApplicationDelegate.SharedInstance.OpenUrl (application, url, sourceApplication, annotation);
}

How to share post in facebook by using Xamarin Forms

I'm currently working on Xamarin and I'm confused with the facebook sharing option in xamarin forms particularly in Xamarin Android the IOS code is
public void ShareOnFacebook(IFacebookDelegate pDele)
{
string[] perm = {"publish_actions"};
if (AccessToken.CurrentAccessToken == null || !AccessToken.CurrentAccessToken.HasGranted("publish_actions"))
{
UIViewController mainController = UIApplication.SharedApplication.KeyWindow.RootViewController;
_manager.LogInWithPublishPermissions(perm, mainController, (result, error) =>
{
if (error != null || result.IsCancelled)
{
}
else {
ShareNow();
}
});
} else {
ShareNow();
}
}
The only thing which stops me is Xamarin Android facebook post sharing.
Can anyone modify this code according to Xamarin Android Or share his/her own code .
I have implemented share for twitter and fb .
iOS version
you can share using native social services from ios and if not available use
OAuth2Authenticator to get access token then post using FB graph
public void ShareViaSocial(string serviceType, string urlToShare)
{
socialKind = serviceType == "Twitter" ? SLServiceKind.Twitter : SLServiceKind.Facebook;
if (SLComposeViewController.IsAvailable(socialKind))
{
_socialComposer = serviceType == "Twitter" ? SLComposeViewController.FromService(SLServiceType.Twitter) : SLComposeViewController.FromService(SLServiceType.Facebook);
_socialComposer.AddUrl(new Uri(urlToShare));
viewController.PresentViewController(_socialComposer, true, () =>
{
_socialComposer.CompletionHandler += (result) =>
{
Device.BeginInvokeOnMainThread(() =>
{
viewController.DismissViewController(true, null);
if (result == SLComposeViewControllerResult.Done)
{ OnShare(this, ShareStatus.Successful); }
else
{ OnShare(this, ShareStatus.NotSuccessful); }
});
};
});
}
//If user doest have fb app and no credential for social services we use fb graph
else if (socialKind == SLServiceKind.Facebook)
{
var auth = new OAuth2Authenticator(
clientId: SharedConstants.FacebookLiveClientId,
scope: SharedConstants.FacebookScopes,
authorizeUrl: new Uri(SharedConstants.FacebookAuthorizeUrl),
redirectUrl: new Uri(SharedConstants.FacebookRedirectUrl));
viewController.PresentViewController((UIViewController)auth.GetUI(), true, null);
auth.AllowCancel = true;
auth.Completed += (s, e) =>
{
//hide the webpage after completed login
viewController.DismissViewController(true, null);
// We presented the UI, so it's up to us to dimiss it on iOS.
if (e.IsAuthenticated)
{
Account fbAccount = e.Account;
Dictionary<string, string> dictionaryParameters = new Dictionary<string, string>() { { "link", urlToShare } };
var requestUrl = new Uri("https://graph.facebook.com/me/feed");
var request = new OAuth2Request(SharedConstants.requestMethodPOST, requestUrl, dictionaryParameters, fbAccount);
request.GetResponseAsync().ContinueWith(this.requestResult);
}
else { OnShare(this, ShareStatus.NotSuccessful); }
};
auth.Error += Auth_Error;
}
//If user doest have twitter app and no credential for social services we use xanarub auth for token and call twitter api for sending tweets
else
{
var auth = new OAuth1Authenticator(
SharedConstants.TwitterConsumerKey,
SharedConstants.TwitterConsumerSecret,
new Uri(SharedConstants.TwitterRequestUrl),
new Uri(SharedConstants.TwitterAuth),
new Uri(SharedConstants.TwitterAccessToken),
new Uri(SharedConstants.TwitterCallBackUrl));
auth.AllowCancel = true;
// auth.ShowUIErrors = false;
// If authorization succeeds or is canceled, .Completed will be fired.
auth.Completed += (s, e) =>
{
// We presented the UI, so it's up to us to dismiss it.
viewController.DismissViewController(true, null);
if (e.IsAuthenticated)
{
Account twitterAccount = e.Account;
Dictionary<string, string> dictionaryParameters = new Dictionary<string, string>() { { "status", urlToShare } };
var request = new OAuth1Request(SharedConstants.requestMethodPOST, new Uri("https://api.twitter.com/1.1/statuses/update.json"), dictionaryParameters, twitterAccount);
//for testing var request = new OAuth1Request("GET",new Uri("https://api.twitter.com/1.1/account/verify_credentials.json "),null, twitterAccount);
request.GetResponseAsync().ContinueWith(this.requestResult);
}
else { OnShare(this, ShareStatus.NotSuccessful); }
};
auth.Error += Auth_Error;
//auth.IsUsingNativeUI = true;
viewController.PresentViewController((UIViewController)auth.GetUI(), true, null);
}
}
Android version
You can use native facebook ShareDialog and if isn't available use OAuth2Authenticator to get access token then post using FB graph
and using OAuth1Authenticator for posing on twitter
public void ShareViaSocial(string serviceType, string urlToShare)
{
ShareDialog di = new ShareDialog(MainActivity.Instance);
var facebookShareContent = new ShareLinkContent.Builder();
facebookShareContent.SetContentUrl(Android.Net.Uri.Parse(urlToShare));
if (serviceType == "Facebook")
{
if (di.CanShow(facebookShareContent.Build(), ShareDialog.Mode.Automatic))
{
di.Show(facebookShareContent.Build());
}
else
{
var auth = new OAuth2Authenticator(
clientId: 'ClientId',
scope: "public_profile,publish_actions",
authorizeUrl: new Uri("https://m.facebook.com/dialog/oauth/"),
redirectUrl: new Uri( "http://www.facebook.com/connect/login_success.html"));
MainActivity.Instance.StartActivity(auth.GetUI(MainActivity.Instance.ApplicationContext));
auth.AllowCancel = true;
auth.Completed += (s, e) =>
{
if (e.IsAuthenticated)
{
Account fbAccount = e.Account;
Dictionary<string, string> dictionaryParameters = new Dictionary<string, string>() { { "link", urlToShare } };
var requestUrl = new Uri("https://graph.facebook.com/me/feed");
var request = new OAuth2Request(SharedConstants.requestMethodPOST, requestUrl, dictionaryParameters, fbAccount);
request.GetResponseAsync().ContinueWith(this.requestResult);
}
else { OnShare(this, ShareStatus.NotSuccessful); }
};
auth.Error += Auth_Error;
}
}
else
{
var auth = new OAuth1Authenticator(
'TwitterConsumerKey',
'TwitterConsumerSecret',
new Uri("https://api.twitter.com/oauth/request_token"),
new Uri("https://api.twitter.com/oauth/authorize"),
new Uri("https://api.twitter.com/oauth/access_token"),
new Uri('TwitterCallBackUrl'));
auth.AllowCancel = true;
// auth.ShowUIErrors = false;
// If authorization succeeds or is canceled, .Completed will be fired.
auth.Completed += (s, e) =>
{
// We presented the UI, so it's up to us to dismiss it.
if (e.IsAuthenticated)
{
Account twitterAccount = e.Account;
Dictionary<string, string> dictionaryParameters = new Dictionary<string, string>() { { "status", urlToShare } };
var request = new OAuth1Request(SharedConstants.requestMethodPOST, new Uri("https://api.twitter.com/1.1/statuses/update.json"), dictionaryParameters, twitterAccount);
//for testing var request = new OAuth1Request("GET",new Uri("https://api.twitter.com/1.1/account/verify_credentials.json "),null, twitterAccount);
request.GetResponseAsync().ContinueWith(this.requestResult);
}
else { OnShare(this, ShareStatus.NotSuccessful); }
};
auth.Error += Auth_Error;
//auth.IsUsingNativeUI = true;
MainActivity.Instance.StartActivity(auth.GetUI(MainActivity.Instance.ApplicationContext));
}
}

Xamarin Async and Await: UI thread is getting blocked

I have this architecture in my project and sometimes UI thread is getting blocked, can someone please explain what is happening with the below code. Thanks
I am making a service call asyncronously from xamarin.forms viewmodel
Following is the flow
View--->ViewModel---ClassA--->ClassB--Make a service call from here
Code
Scenario 1
public partial class HomePage : ContentPage
{
private HomeVM model;
public HomePage()
{
InitializeComponent();
model = new HomeVM();
model.MainText = ReturnBool().Result;
this.BindingContext = model;
}
public async Task<string> ReturnBool()
{
IsBusy = true;
var r = await new WS().ReturnBool();
IsBusy = false;---------------------------------------Not hitting the breakpoint here
return r;
}
}
public interface IWS
{
Task<string> ReturnBool();
}
public class WS : IWS
{
public Task<string> ReturnBool()
{
return ServiceOperations.ReturnBool();
}
}
internal class ServiceOperations
{
public async static Task<string> ReturnBool()
{
var uri = new Uri(string.Format("http://testmyapi.azurewebsites.net/", string.Empty));
try
{
HttpClient client = new HttpClient();
client.BaseAddress = uri;
HttpResponseMessage response = null;
response = await client.GetAsync("/api/Values/Get");
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
string str = JsonConvert.DeserializeObject<string>(content);
return str;
}
else {
return null;
}
}
catch (Exception)
{
return null;
}
}
}
Scenario 2
public partial class HomePage : ContentPage
{
private HomeVM model;
public HomePage()
{
InitializeComponent();
model = new HomeVM();
this.BindingContext = model;
}
}
public class HomeVM : BaseVM
{
private string mainText;
public string MainText
{
get { return mainText; }
set
{
mainText = value;
RaisePropertyChanged("MainText");
}
}
public HomeVM()
{
MainText = ReturnBool().Result;
}
public async Task<string> ReturnBool()
{
IsBusy = true;
var r = await new WS().ReturnBool();
IsBusy = false;---------------------------------------Not hitting the breakpoint here
return r;
}
}
public interface IWS
{
Task<string> ReturnBool();
}
public class WS : IWS
{
public Task<string> ReturnBool()
{
return ServiceOperations.ReturnBool();
}
}
internal class ServiceOperations
{
public async static Task<string> ReturnBool()
{
var uri = new Uri(string.Format("http://testmyapi.azurewebsites.net/", string.Empty));
try
{
HttpClient client = new HttpClient();
client.BaseAddress = uri;
HttpResponseMessage response = null;
response = await client.GetAsync("/api/Values/Get");
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
string str = JsonConvert.DeserializeObject<string>(content);
return str;
}
else {
return null;
}
}
catch (Exception)
{
return null;
}
}
}
You are using ReturnBool().Result in the constructor. The return call will block your UI thread. Move that code to the controller action methods without using ".Result" part. Ensure that the methods are async and always return a Task.

How can I upload image and post some datas to MVC4 wep api method?

I have tried for days but I couldn't reach any successful result. I need to post images with their information (s.t. created user name).
This is my method;
[HttpPost]
public Task<HttpResponseMessage> PostFile(string createdByName)
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = System.Configuration.ConfigurationSettings.AppSettings["TempUploadDir"];
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
AddImages(provider.BodyPartFileNames);
string file1 = provider.BodyPartFileNames.First().Value;
// this is the file name on the server where the file was saved
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
}
);
return task;
}
And this my TypeFormatterClass which is added global.asax
public class MultiFormDataMediaTypeFormatter : FormUrlEncodedMediaTypeFormatter
{
public MultiFormDataMediaTypeFormatter()
: base()
{
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
}
protected override bool CanReadType(Type type)
{
return true;
}
protected override bool CanWriteType(Type type)
{
return false;
}
protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext)
{
var contents = formatterContext.Request.Content.ReadAsMultipartAsync().Result;
return Task.Factory.StartNew<object>(() =>
{
return new MultiFormKeyValueModel(contents);
});
}
class MultiFormKeyValueModel : IKeyValueModel
{
IEnumerable<HttpContent> _contents;
public MultiFormKeyValueModel(IEnumerable<HttpContent> contents)
{
_contents = contents;
}
public IEnumerable<string> Keys
{
get
{
return _contents.Cast<string>();
}
}
public bool TryGetValue(string key, out object value)
{
value = _contents.FirstDispositionNameOrDefault(key).ReadAsStringAsync().Result;
return true;
}
}
}
When I post images and "createdByName" I can reach images but I couldn't parameters. How can I do this?
Thank you.
To get your createdByName field, inside your ContinueWith :
var parts = o.Result;
HttpContent namePart = parts.FirstDispositionNameOrDefault("createdByName");
if (namePart == null)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
string name = namePart.ReadAsStringAsync().Result;
For a more detailed example, see :
http://www.asp.net/web-api/overview/working-with-http/html-forms-and-multipart-mime#multipartmime