How to get json response with WebView2 - c#

I wanna get a json from the response's body of this API:
// http://localhost:3000/api/auth/[token]
export default function Auth(request, response) {
response.status(200).json({ token: request.query})
}
Trying the WebView.CoreWebView2.WebResourceResponseReceived event fires just once and the event arg's request Uri parameter is "http://localhost:3000/favicon.ico".
How can I get the response content?
What I did:
public partial class SignInUserControl : UserControl
{
public SignInUserControl()
{
InitializeComponent();
InitWebView();
}
async void InitWebView()
{
await WebView.EnsureCoreWebView2Async(null);
WebView.CoreWebView2.WebResourceResponseReceived += CoreWebView2_WebResourceResponseReceived;
}
async void CoreWebView2_WebResourceResponseReceived(object sender, CoreWebView2WebResourceResponseReceivedEventArgs e)
{
try
{
Stream stream = await e.Response.GetContentAsync();
TextReader tr = new StreamReader(stream);
string re = tr.ReadToEnd();
}
catch { }
}
}
What I expect:
http://localhost:3000/api/auth/42sad87aWasFGAS
re = {"token":"42sad87aWasFGAS"} // From CoreWebView2_WebResourceResponseReceived method
ps: The WebViewer2 Control is working. So I don't think the problem is related to its initialization.
working example

The problem really was the WebView initialization. 🤦‍♂️
Thanks to #user09938 and #david-risney
What did I do?
I removed the Source property from the Xaml and made these changes:
public partial class SignInUserControl : UserControl
{
public SignInUserControl()
{
InitializeComponent();
InitwebView();
}
private void InitwebView()
{
WebView.CoreWebView2InitializationCompleted += WebView_CoreWebView2InitializationCompleted;
WebView.EnsureCoreWebView2Async(null).GetAwaiter();
WebView.Source = new Uri("http://localhost:3000/api/auth/NelsonHenrique");
}
private void WebView_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
{
WebView.CoreWebView2.WebResourceResponseReceived += CoreWebView2_WebResourceResponseReceived;
}
private void CoreWebView2_WebResourceResponseReceived(object sender, CoreWebView2WebResourceResponseReceivedEventArgs e)
{
var result = e.Response.GetContentAsync().GetAwaiter();
result.OnCompleted(() =>
{
try
{
var res = result.GetResult();
StreamReader reader = new StreamReader(res);
string text = reader.ReadToEnd();
// text: "{\"token\":\"NelsonHenrique\"}"
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
}
}

Related

C# List.Add System.InvalidOperationException

I am handling an event from a child form in its parent form, and when I try adding items from the list contained within the event args of the handler (ScraperForm_SiteScraped in the code below), I am receiving the exception System.InvalidOperationException in my console.
Interestingly enough, it seems to succeed on the first add, but no subsequent attempts.
public partial class ProxyTesterView : UserControl
{
private BindingList<Proxy> proxies = new BindingList<Proxy>();
private BindingList<ProxyJudge> pudges = new BindingList<ProxyJudge>();
private BindingList<ProxyTest> tests = new BindingList<ProxyTest>();
private PauseOrCancelTokenSource pcts = new PauseOrCancelTokenSource();
private ProxyScraperForm scraperForm = new ProxyScraperForm();
public ProxyTesterView()
{
InitializeComponent();
proxies.ListChanged += Proxies_ListChanged;
scraperForm.SiteScraped += ScraperForm_SiteScraped;
}
private void Proxies_ListChanged(object sender, ListChangedEventArgs e)
{
ProxiesDataGridView.RowCount = proxies.Count;
}
private void AddFromScraperToolStripMenuItem_Click(object sender, EventArgs e)
{
scraperForm.Show();
}
private void ScraperForm_SiteScraped(object sender, SiteScrapedEventArgs e)
{
foreach (var proxy in e.ScrapedProxies)
{
proxies.Add(proxy);
}
}
}
Child Form
public partial class ProxyScraperForm : Form
{
private BindingList<IProxyScraperSite> sites = new BindingList<IProxyScraperSite>();
public int ScrapeInterval { get; set; } = 60000;
public event EventHandler<SiteScrapedEventArgs> SiteScraped;
public ProxyScraperForm()
{
InitializeComponent();
sites.Add(new ProxyScraperSiteUsProxyOrg());
sites.Add(new ProxyScraperSiteFreeProxyListNet());
sites.Add(new ProxyScraperSiteFreeProxyListsNet());
sites.Add(new ProxyScraperSiteHideMyName());
sites.Add(new ProxyScraperSiteHidester());
ScraperDataGridView.DataSource = sites;
}
private void ScrapeButton_Click(object sender, EventArgs e)
{
foreach (var site in sites)
{
Task.Run(async () =>
{
while (true)
{
var driver = SeleniumUtility.CreateDefaultFirefoxDriver();
var newProxies = await site.ScrapeAsync(driver);
driver.Quit();
OnSiteScraped(newProxies);
await Task.Delay(5000);
site.Status = $"Waiting {ScrapeInterval / 1000} seconds...";
await Task.Delay(ScrapeInterval);
}
});
}
}
private void OnSiteScraped(List<Proxy> scrapedProxies)
{
if (SiteScraped != null)
{
SiteScraped(this, new SiteScrapedEventArgs(scrapedProxies));
}
}
}
From our comments, turns out that this was a threading issue. As a good practice, always use a try/catch block when there's a chance that an exception can occur in a block of code. :)
Also, if you're using Visual Studio, you can make VS break on more exceptions by pressing CTRL+ALT+E and selecting the checkboxes. You can read more about exception breaking here.

Get Value from async Method?

I want to have the returned value from the send Operation which is a string and use it in the public MainPage() section. I tried this way, bot doesn´t work. Any idea how to get this value out of the send() Method?
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
string stringData = "";
stringData = "aktion=getBenutzer&name=" + Login.getBenutzername();
//send(stringData);
//textBlock1.Text = getContentOfSendOperation();
button.Foreground = Einstellungen.getBrush();
button1.Foreground = Einstellungen.getBrush();
button2.Foreground = Einstellungen.getBrush();
stringData = "aktion=getMitarbeiterListe";
//string mitarbeiterListe = getContentOfSendOperation();
var task = send(stringData);
string mitarbeiterListe = task.Result;
textBlock1.Text = mitarbeiterListe;
//comboBox.Items.Add()
}
public Frame globalFrame { get { return _mainFrame; } }
private void button_Click(object sender, RoutedEventArgs e)
{
_mainFrame.Navigate(typeof(Datenbank));
}
public async Task<String> send(string stringData)
{
System.Net.Http.HttpClient oHttpClient = new System.Net.Http.HttpClient();
Uri uri = new Uri("*********");
oHttpClient.DefaultRequestHeaders.UserAgent.ParseAdd("moralsKite/DesktopTestClient");
var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Post, uri);
request.Content = new StringContent(stringData, Encoding.UTF8, "application/x-www-form-urlencoded");
var reponse = await oHttpClient.SendAsync(request);
if (reponse.IsSuccessStatusCode)
{
return "??";
//return await reponse.Content.ReadAsStringAsync();
}
return "!!";
}
private void button1_Click_1(object sender, RoutedEventArgs e)
{
_mainFrame.Navigate(typeof(Einstellungen));
}
private void button2_Click_1(object sender, RoutedEventArgs e)
{
_mainFrame.Navigate(typeof(Ueber));
}
}
}
You cannot run asynchronous operation and await it in constructor. In your example the task can run little longer (varying on signal and so on), the constructor of a class should be fast. Better subscribe to one of the page's events like Loaded and put your work there:
public MainPage()
{
this.InitializeComponent();
// rest of code
this.Loaded += MainPage_Loaded;
}
private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
// Following Peter Torr's comment - Loaded event can be fired multiple times
// for example once you navigate back to the page
this.Loaded -= MainPage_Loaded; // deregister from event if you want to run it once
stringData = "aktion=getMitarbeiterListe";
// in your send method uncomment the line:
// return await reponse.Content.ReadAsStringAsync();
// then it will return asynchronously the content as string and can be used like this:
string mitarbeiterListe = await send(stringData);
}
Events can be async then there shouldn't be problems, you may also implement an information for user that something is loading in the background.
Instead of :
var task = send(stringData);
string mitarbeiterListe = task.Result;
use
string mitarbeiterListe = await send(stringData);
Make the method where you call send data async.
I do not recommend to call gathering of data in constructor.

Windows Phone 8.1 Currency converter Json

I got a school project. I have to make a currency converter and I got stuck. I found something on the Code Project web site, but I am new at this and I do not really know how to implement it in my project.
I tried something like `
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
class WebClient
{
internal string DownloadString(string url)
{
throw new NotImplementedException();
url = "https://openexchangerates.org/api/latest.json?app_id=ae11142304694b10a1dbf2d25933a333";
var currencyRates = _download_serialized_json_data<App9.CurrencyRates>(url);
}
}
public static T _download_serialized_json_data<T>(string url) where T : new()
{
var w = new WebClient();
{
//using (var w = new WebClient()) {
var json_data = string.Empty;
// attempt to download JSON data as a string
try
{
json_data = w.DownloadString(url);
}
catch (Exception) { }
// if string with JSON data is not empty, deserialize it to class and return its instance
return !string.IsNullOrEmpty(json_data) ? JsonConvert.DeserializeObject<T>(json_data) : new T();
}
}
private void comboBoxTo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
private void convertButton_Click(object sender, RoutedEventArgs e)
{
if (amountTb.Text == string.Empty)
{
afisareTb.Text = "Scrieti o valoare";
}
else
{
var currencyRates = _download_serialized_json_data<CurrencyRates>("https://openexchangerates.org/api/latest.json?app_id=YOUR_APP_ID ");
}
}
}
`
I do not have any errors, it is just that, when I press on converter button from my app, nothing happens.

Set TextBlock from Async Completion Handler

I would like to set the text on the MainPage of my app, based on the response of an Async call to Web Service.
Im getting a "The application called an interface that was marshalled for a different thread". So I know that I need to execute the
MainPage.TB_Response.text = response;
On the Primary/Main Thread, but I am unsure as to how i would go about this
Edit: Here is my Response Handler
private void ReadResponse(IAsyncResult asyncResult)
{
System.Diagnostics.Debug.WriteLine("ReadResponse");
try
{
// The downloaded resource ends up in the variable named content.
var content = new MemoryStream();
// State of request is asynchronous.
//RequestState myRequestState = (RequestState)asyncResult.AsyncState;
HttpWebRequest myHttpWebRequest2 = (HttpWebRequest)asyncResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)myHttpWebRequest2.EndGetResponse(asyncResult);
//do whatever
using (Stream responseStream = response.GetResponseStream())
{
responseStream.CopyTo(content);
byte[] data = content.ToArray();
if (data.Length > 0)
{
string temp = System.Text.Encoding.UTF8.GetString(data, 0, data.Length);
MainPage.TB_Reponse.Text = temp;
System.Diagnostics.Debug.WriteLine(temp);
}
}
}
catch (WebException e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}
}
Edit2: My MainPage Class
public sealed partial class MainPage : Page
{
public static TextBlock TB_Reponse;
public MainPage()
{
this.InitializeComponent();
MainPage.TB_Reponse = this.TB_Response;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private void BTN_Login_Click(object sender, RoutedEventArgs e)
{
...
}
}
So I found this solution to solve my problem
Delegate 'System.Action<object>' does not take 0 arguments - C# - Task
I Setup my ServerRequest object to store the current synchronisation context
ui = TaskScheduler.FromCurrentSynchronizationContext();
then in my Response Handler I run the following:
Task.Factory.StartNew(() =>
{
MainPage.TB_Reponse.Text = temp; //This is run on the same thread as the UI
},System.Threading.CancellationToken.None , TaskCreationOptions.None, instance.ui);

How can I redirect the stdout of IronPython in C#?

I have the following:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button3_Click(object sender, EventArgs e)
{
try
{
var strExpression = #"
import sys
sys.stdout=my.write
print 'ABC'
";
var engine = Python.CreateEngine();
var scope = engine.CreateScope();
var sourceCode = engine.CreateScriptSourceFromString(strExpression);
scope.SetVariable("my", this);
var actual = sourceCode.Execute<string>(scope);
textBox1.Text += actual;
}
catch (System.Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
public void write(string s)
{
textBox1.Text += s;
}
}
But I am getting an Exception that says there is no write.
What am I doing incorrectly?
You can set a stream and a textwriter directly from c#:
engine.Runtime.IO.SetOutput(stream, txtWriter);
engine.Runtime.IO.SetErrorOutput(stream, txtWriter);
To redirect the output for example you could override TextWriter class with a new one writing on your textbox.
e.g.
in my application I did an override of StreamWriter class that rises events when something is written on the stream (here just a part of the code):
public class MyEvtArgs<T> : EventArgs
{
public T Value
{
get;
private set;
}
public MyEvtArgs(T value)
{
this.Value = value;
}
}
public class EventRaisingStreamWriter : StreamWriter
{
#region Event
public event EventHandler<MyEvtArgs<string>> StringWritten;
#endregion
#region CTOR
public EventRaisingStreamWriter(Stream s):base(s)
{ }
#endregion
#region Private Methods
private void LaunchEvent(string txtWritten)
{
if (StringWritten != null)
{
StringWritten(this, new MyEvtArgs<string>(txtWritten));
}
}
#endregion
#region Overrides
public override void Write(string value)
{
base.Write(value);
LaunchEvent(value);
}
public override void Write(bool value)
{
base.Write(value);
LaunchEvent(value.ToString());
}
// here override all writing methods...
#endregion
}
Then in your application you should just do something like:
MemoryStream ms = new MemoryStream();
EventRaisingStreamWriter outputWr = new EventRaisingStreamWriter(ms);
outputWr.StringWritten += new EventHandler<MyEvtArgs<string>>(sWr_StringWritten);
var engine = Python.CreateEngine();
engine.Runtime.IO.SetOutput(ms, outputWr);
engine.CreateScriptSourceFromString("print 'hello world!'").Execute();
void sWr_StringWritten(object sender, MyEvtArgs<string> e)
{
textBox1.Text += e.Value;
}
Your example is close to working.
The problem you saw is because sys.stdout=my.write should be sys.stdout=my.
It also appears that Python expects to find a boolean softspace attribute.
I have made these two changes in the code below. Hopefully this should now work as you expected.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button3_Click(object sender, EventArgs e)
{
try
{
var strExpression = #"
import sys
sys.stdout=my
print 'ABC' ";
var engine = Python.CreateEngine();
var scope = engine.CreateScope();
var sourceCode = engine.CreateScriptSourceFromString(strExpression);
scope.SetVariable("my", this);
var actual = sourceCode.Execute(scope);
textBox1.Text += actual;
} catch (System.Exception ex) {
MessageBox.Show(ex.ToString());
}
}
public bool softspace;
public void write(string s)
{
textBox1.Text += s;
}
}
This worked fine for me
pyRuntime = Python.CreateRuntime();
pyRuntime.IO.SetOutput(Console.OpenStandardOutput(), new UTF8Encoding(true, false));

Categories