I've a simple Xamarin.Forms page with a WebView in it, working well.
Now I want to call POST-Requests (sending form-data) for an URL.
Do I have to create 3 different views for Android, iOS and UWP to achieve that - or is there a way to implement the C# post-request code only once in the shared coded of my Xamarin-Forms project?
As #hvaughan3 says you can use HttpClient, like so (from the shared code of your Xamarin Forms project):
private static HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(30) };
HttpContent content = new StringContent(JsonConvert.SerializeObject(objectToPost), Encoding.UTF8, "application/x-www-form-urlencoded");
var response = await client.PostAsync(new Uri("http://your.url"), content);
if (response.IsSuccessStatusCode) {
var responseFromServer = await response.Content.ReadAsStringAsync();
}
else {
// handle errors
}
Use HTTPcLient for form-URLencoded post request and then create a Custom Webview to handle cookies. Use the CookieWebView Nuget Package created by seansparkman.
Please check https://www.nuget.org/packages/CookieWebView/
POST Request Over Webview
For WkWebview
[assembly: ExportRenderer(typeof(PaymentWebview), typeof(PaymentWebViewRenderer))]
namespace MMFInvestorApp.iOS.Utils
{
public class PaymentWebViewRenderer : WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (NativeView != null)
{
var request = new NSMutableUrlRequest(new NSUrl(new NSString(paymentwebview.url))); //Your Url
request.HttpMethod = "POST";
request.Body = NSData.FromString(paymentwebview.data); //Data for POST
request["Content-Length"] = req.Body.Length.ToString();
request["Content-Type"] = "application/x-www-form-urlencoded charset=utf-8";
LoadRequest(request);
}
}
}
}
For UIWebview (Deprecated from April 2020
[assembly: ExportRenderer(typeof(PaymentWebview), typeof(PaymentWebViewRenderer))]
namespace MMFInvestorApp.iOS.Utils
{
public class PaymentWebViewRenderer : WebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (NativeView != null)
{
var paymentwebview = Element as PaymentWebview;
var request = new NSMutableUrlRequest(new NSUrl(new NSString(paymentwebview.url)));//Your Url
request.Body = paymentwebview.data; //Data for POST
request.HttpMethod = "POST";
LoadRequest(request);
}
}
}
}
Related
I am developing a Blazor WASM application in .NET6.
In Blazor is possible to make a call from Client component
#code {
await Http.GetFromJsonAsync<Model>("/api/model/view/" + modelId);
}
that triggers a controller in the Server project
[HttpGet, Route("/api/model/view/{id:int}")]
public async Task<Model> GetModel(int modelId)
{
try { return await Task.FromResult(_IModel.GetModel(id)); }
catch { throw; }
}
But is it possible to post from a client project via cs class like following:
HttpClient httpClient = new HttpClient();
var content = new StringContent(JsonConvert.SerializeObject(authcookie_value), Encoding.UTF8, "application/json");
httpClient.BaseAddress = new(Parameters.urlstring);
var result = httpClient.PostAsync("/api/auth/refresh", content);
And the controller on the Server project to be fired:
[HttpPost, Route("/api/auth/refresh")]
public IActionResult AuthRefresh(StringContent httpContent)
{
try
{
}
catch { throw; }
}
The above example is not working because the controller is not fired when the call is thiggered.
Any ideas?
I have a Xamarin forms mobile app where the user authenticates using a post REST API and I am going to save the returned ASP.NET session ID and the authentication cookie to later pass it to my WebView in the main page to load a web page that needs authentication. For this, I created a custom web view renderer and followed some guides that suggested how to pass the cookie in the cookie container to the WebView for each request. But that does not work and I get to the login page of our website. Please advise.
WebView Renderer (IOS):
[assembly: ExportRenderer(typeof(CookieWebView), typeof(CookieWebViewRenderer))]
namespace perfectmobile.iOS
{
public class CookieWebViewRenderer: WebViewRenderer
{
public CookieWebView CookieWebView
{
get { return Element as CookieWebView; }
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
Delegate = (UIKit.IUIWebViewDelegate)new WebViewDelegate(CookieWebView);
}
}
}
internal class WebViewDelegate : UIWebViewDelegate
{
private CookieWebView _cookieWebView;
public WebViewDelegate(CookieWebView cookieWebView)
{
_cookieWebView = cookieWebView;
}
public override bool ShouldStartLoad(UIWebView webView, NSUrlRequest request, UIWebViewNavigationType navigationType)
{
// Set cookies here
var cookieJar = NSHttpCookieStorage.SharedStorage;
cookieJar.AcceptPolicy = NSHttpCookieAcceptPolicy.Always;
//clean up old cookies
foreach (var aCookie in cookieJar.Cookies)
{
cookieJar.DeleteCookie(aCookie);
}
//set up the new cookies
var jCookies = _cookieWebView.Cookies.GetCookies(request.Url);
IList<NSHttpCookie> eCookies =
(from object jCookie in jCookies
where jCookie != null
select (Cookie)jCookie
into netCookie
select new NSHttpCookie(netCookie)).ToList();
foreach (var ck in eCookies)
{
cookieJar.SetCookie(ck);
}
return true;
}
public override void LoadFailed(UIWebView webView, NSError error)
{
// TODO: Display Error Here
Debug.WriteLine("ERROR: {0}", error.ToString());
}
public override void LoadingFinished(UIWebView webView)
{
}
}
}
//===================PCL project Cookie webview ========//
public class CookieWebView : WebView
{
public static readonly BindableProperty CookiesProperty = BindableProperty.Create(
propertyName: "Cookies",
returnType: typeof(CookieContainer),
declaringType: typeof(CookieWebView),
defaultValue: default(string));
public CookieContainer Cookies
{
get { return (CookieContainer)GetValue(CookiesProperty); }
set { SetValue(CookiesProperty, value); }
}
public CookieWebView()
{
Cookies = new CookieContainer();
}
}
//========= Login ======//
var handler = new HttpClientHandler();
handler.CookieContainer = UserInfo.CookieContainer;
HttpClient client = new HttpClient(handler);
HttpContent content = new StringContent("");
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
Uri uri = new Uri(LoginUrl);
var response = client.PostAsync(uri,content);
var responseResult = response.Result;
if (responseResult.IsSuccessStatusCode)
{
IEnumerable<Cookie> responseCookies = UserInfo.CookieContainer.GetCookies(uri).Cast<Cookie>();
foreach (Cookie cookie in responseCookies)
{
UserInfo.CookieContainer.Add(uri, cookie);
}
}
//======== User Info =======//
public class UserInfo
{
public static CookieContainer CookieContainer = new CookieContainer();
}
// ======== Main Page Xaml =======//
<local:CookieWebView x:Name="webView" Source="Url of the website page " WidthRequest="1000" HeightRequest="1000" />
//========= Main page.cs ==========//
public partial class MainTabbedPage : ContentPage
{
public MainTabbedPage()
{
InitializeComponent();
webView.Cookies = UserInfo.CookieContainer;
}
You need to create a custom control in your PCL-Project and then add a custom webview for each platform. The platform specific implementation then gets the cookies and you can use it from your pcl-webview.
Android
var cookieHeader = CookieManager.Instance.GetCookie(url);
iOS
NSHttpCookieStorage storage = NSHttpCookieStorage.SharedStorage;
And you can check https://github.com/seansparkman/CookiesWebView for more details .
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;
}
}
I am trying to receive one simple sentence from a webservice, but I have something wrong.
This is my async task to request from the webservice:
private async Task<string> GetData (string url)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create (new Uri(url));
request.ContentType = "text/plain";
request.Method = "GET";
using (WebResponse response = await request.GetResponseAsync())
{
using (Stream stream = response.GetResponseStream())
{
string doc = await Task.Run(() => stream.ToString());
return doc;
}
}
}
And this is my button:
cmd02.Click += async (sender, e) => {
string sentence = await GetData(url);
txt01.Text = sentence;
};
I get only "System.Net.WebConnectionStream" into my TextView and donĀ“t know which function I should use. Or maybe everthing is wrong?
Maybe somebody has an idea?
public static async Task<string> SendGetRequestAsync (string url) {
string responseString = "";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create (url);
request.Method = WebRequestMethods.Http.Get;
HttpWebResponse response;
await Task.Run (() => {
try {
response = request.GetResponse () as HttpWebResponse;
using (var reader = new StreamReader (response.GetResponseStream ())) {
responseString = reader.ReadToEnd ();
}
} catch (WebException ex) {
Console.WriteLine (ex);
}
});
return responseString;
}
My windows phone client sends a piece of JSON that represents the Name and Score... Should I be using the Class from the Web API instead ? What is the code to send the object to the server rather than the raw json?
private void btnCreateSampleScore_Click(object sender, RoutedEventArgs e)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://punkoutersoftware.azurewebsites.net");
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("Bob", "2.65")
});
var result = client.PostAsync("/api/DrunkMeterScore", content).Result;
string resultContent = result.Content.ReadAsStringAsync().Result;
Console.WriteLine(resultContent);
//DrunkMeterScore dms = new DrunkMeterScore();
//dms.Name = "Bob";
//dms.Score = 2.42;
}
}
The server is using the plain Web API template
// POST api/DrunkMeterScore
public HttpResponseMessage PostDrunkMeterScore(DrunkMeterScore drunkmeterscore)
{
if (ModelState.IsValid)
{
db.DrunkMeterScores.Add(drunkmeterscore);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, drunkmeterscore);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = drunkmeterscore.DrunkMeterScoreId }));
return response;
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
edit1:
I tried both of these.. They both hang and never come back during the PostAsync calls :(
var client = new HttpClient();
client.BaseAddress = new Uri("http://punkoutersoftware.azurewebsites.net");
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var score = new DrunkMeterScore() { Name = "Joe", Score = 2.67 };
//MediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
//HttpContent content = new ObjectContent<DrunkMeterScore>(score, jsonFormatter);
//var resp = client.PostAsync("api/DrunkMeterScore", content).Result;
//Uri scoreUri = null;
//HttpResponseMessage response = client.PostAsJsonAsync("api/DrunkMeterScore", score).Result;
//if (response.IsSuccessStatusCode)
//{
// scoreUri = response.Headers.Location;
//}
//else
//{
// Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
//}
To serialize objects you need the ObjectContent class which for the wp8 is only available as an alpha release.
Use the package console to do,
Install-Package Microsoft.AspNet.WebApi.Client -Pre
Should be something like this. I did this from memory so YMMV.
DrunkMeterScore dms = new DrunkMeterScore();
dms.Name = "Bob";
dms.Score = 2.42;
var content = new ObjectContent(dms, new JsonMediaTypeFormatter());
var result = client.PostAsync("/api/DrunkMeterScore", content).Result;