Use Puppeteer Sharp in Blazor webassembly - c#

I want to save razor page as pdf for user.I used the following code in Blazer Web Assembly, but an error was observed.
using var browserFetcher = new BrowserFetcher();
await browserFetcher.DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
await using var page = await browser.NewPageAsync();
await page.GoToAsync("http://www.google.com"); // In case of fonts being loaded from a CDN, use WaitUntilNavigation.Networkidle0 as a second param.
await page.EvaluateExpressionHandleAsync("document.fonts.ready"); // Wait for fonts to be loaded. Omitting this might result in no text rendered in pdf.
await page.PdfAsync("my.pdf");
Error:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: System.Net.WebClient is not supported on this platform. Use System.Net.Http.HttpClient instead.
System.PlatformNotSupportedException: System.Net.WebClient is not supported on this platform. Use System.Net.Http.HttpClient instead.

Related

Adding SSL cert causes 404 only in browser calls

I am working in an internal corporate environment. We have created a webapi installed on iis on port 85. We call this from another MVC HelperApp on port 86. It all works as expected. Now we want to tighten security and add an SSL cert to iis on port 444 and bind it to our API.
Initially we test it with Postman, SoapUI, and a C# console app and it all works. Now we try calling it from our MVC HelperApp and it returns a 404 sometimes.
Deeper debugging; I put the code into a C# DLL (see below). Using the console app I call the Dll.PostAPI and it works as expected. Now I call that same Dll.PostAPI from the MVC HelperApp and it won't work. When I step through the code I make it as far as this line await client.PostAsync(url, data); and the code bizarrely ends, it doesn't return and it doesn't throw an exception. Same for Post and Get. I figure it makes the call and nothing is returned, no response and no error.
Also, if I change the url to "https://httpbin.org/post" or to the open http port85 on iss it will work. I have concluded that the C# code is not the problem (but I'm open to being wrong).
Therefore I have come to the conclusion that for some reason the port or cert is refusing calls from browsers.
We are looking at:
the "Subject Alternative Name" but all the examples show
WWW.Addresses which we are not using.
the "Friendly Name" on the cert creation.
and CORS Cross-Origin Resource Sharing.
These are all subjects we lack knowledge in.
This is the calling code used exactly the same in the console app and the web app:
var lib = new HttpsLibrary.ApiCaller();
lib.makeHttpsCall();
This is what's in the DLL that gets called:
public async Task<string> makeHttpsCall()
{
try
{
List<Quote> quotes = new List<Quote>();
quotes.Add(CreateDummyQuote());
var json = JsonConvert.SerializeObject(quotes);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var url = "https://httpbin.org/post"; //this works in Browser
//url = "https://thepath:444//api/ProcessQuotes"; //444 DOES NOT WORK in browsers only. OK in console app.
//url = "http://thepath:85/api/ProcessQuotes"; //85 works.
var client = new HttpClient();
var response = await client.PostAsync(url, data); //<<<this line never returns when called from browser.
//var response = await client.GetAsync(url); //same outcome for Get or Post
var result = await response.Content.ReadAsStringAsync();
return result;
}
catch (Exception ex)
{
throw;
}
}

Simulating a web browser C#

I'm trying to extract data with HtmlAgilityPack from various websites by downloading their HTML code with client.DownloadStringAsync(). This has been working flawlessy so far, but I recently encountered a problem with one of the websites I tried to download this way. Instead of the actual content of the site which can be seen in a browser, it just said "Loading...". After checking networking in chrome, it seems that the GET request just gets a raw HTML, which doesn't actually include any of the data I need and instead it is mostly initialized by Javascript. I found the Selenium WebDriver which should do what I want it to, however I think it will conflict if I try to run all of that on a Raspberry Pi (which is what I'm designing this project for). If there are any alternatives, let me know or if it works on unix based systems too. Here is the Http Getter I used so far:
private async Task<string> HttpGet(string URL) {
using var client = new HttpClient();
using var request = new HttpRequestMessage {RequestUri = new Uri(URL), Method = HttpMethod.Get};
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
if (response.StatusCode == HttpStatusCode.NotFound) return "404";
return await response.Content.ReadAsStringAsync();
}
After attempting to use the aformentioned NuGet package with Chrome, I managed to get heaps of different errors which were all foreign to me. Here's my code:
private string SeleniumGet(string URL) {
ChromeOptions options = new ChromeOptions();
options.AddArgument("headless");
options.BinaryLocation = Directory.GetCurrentDirectory() + #"\chromedriver.exe"; //Probably not gonna work on an RPi
using ChromeDriver driver = new ChromeDriver(options);
driver.Navigate().GoToUrl(URL);
return driver.PageSource; //Unsure if this is source code, doesn't really matter for now - program crashes when initiating chrome driver
}
Currently am stuck on it saying Invalid --log-level value., but I had other ones like unknown error: DevToolsActivePort file doesn't exist. however, I can't reproduce that one. Currently only the invalid log level error pops up, which crashes when constructing the ChromeDriver. Thanks for the help in advance. If the site I try to access matters, This is it. Looking at the GET request in networking shows the blank page with loading on it.

Blazor server-side application api request issue httpClient.PostAsync IHttpClientFactory

I am developing a Blazor server-side application.
The api call generates and downloads a pdf file.
No error is displayed on the client side but I can see from the server logs that the API call is not being processed. The only error message I can identify is StatusCode: 415, ReasonPhrase: Unsupported Media Type, Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers.
GetAsync is working fine
var response = await _httpClient.GetAsync($"xxx/yyyyy/zzzzz");
The only significant issue I have for now is with serialisation and deserialisation using the new System.Text.Json.Serialization and for now I have worked around this by developing my own serialisation and deserialisation routines which work fine and I will use them until I have resolved the issues with System.Text.Json.Serialization.
My question relates to PostAsynch which I cannot get to work. I am using Basic Authentication for now and sending a JSON body. I am calling APIs which are live and have been working fine with other applications calling them. They also work fine in Postman, I just cannot get them to work with Blazor.
//The relevant Startup code is as follows
services.AddHttpClient<Services.ApiServicerw>(client =>
{
client.BaseAddress = new Uri("https://www.xxxxxxxxxx.com");
});
//The code in the .razor page is as follows
#page "/runreport2"
#using BlazorApp1.Services
#inject ApiServicerw ApiService1
#using System.IO
<h1>Report</h1>
<p>This component demonstrates running a report</p>
#code {
protected override async Task OnInitializedAsync()
{
var fresult = await ApiService1.GetContactsAsync();
}
}
//An extract of the code in the Services class is as follows
_httpClient.DefaultRequestHeaders.Authorization = new
System.Net.Http.Headers.AuthenticationHeaderValue("Basic", encoded);
var stringContent = new StringContent(jsonString);
_httpClient.DefaultRequestHeaders.Accept.Add(new
System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var response = await _httpClient.PostAsync($"api/xxxx/yyyyyy",stringContent);
I have resolved this. I needed to change this line as follows
var stringContent = new StringContent(jsonString,System.Text.Encoding.UTF8, "application/json");

azure website running in browser but not in PCL

I have a azure website which was running in browser. If I go to a particular page like my.azurewebsites.net/rest/api it's showing response on webpage. But if I used it in my PCL code like this
using (var httpClient = new HttpClient() { MaxResponseContentBufferSize = int.MaxValue })
{
HttpResponseMessage response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
Than its showing inner exception "The remote name could not be resolved".
Addition information; "An error occurred while sending request"
3 days before it was working fine. Now I don't know what happened and how to fix.

How to download a webpage in MetroStyle app (WinRT) and C#

I'm creating a MetroStyle app and I want to use a website API that is based on the HTTP Get methods. For instance to login I should download the XML returned by this URL:
websitehost.com/api/login.php?u=username&p=password
The problem is that the new MetroStyle apps won't let me to use many of the methods I've been using for years in .Net so how can I download the returned XML document and parse it?
You might be searching for this:
public async Task<string> DownloadPageStringAsync(string url)
{
HttpClientHandler handler = new HttpClientHandler()
{ UseDefaultCredentials = true, AllowAutoRedirect = true };
HttpClient client = new HttpClient(handler);
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
You can use either the Windows.Data.Xml.Dom.XmlDocument.LoadFromUriAsync(Uri) method to automatically acquire and parse the XML, or you could manually use a Windows.Networking.BackgroundTransfer.DownloadOperation instance to call the web service and acquire the data, and Windows.Data.Xml.Dom.XmlDocument.LoadXml(string) to parse the data.
You should be able to use
var data = await (new System.Net.Http.HttpClient()).GetAsync(new Uri("http://wherever"));
And then do whatever you need with the data, including loading it with XmlDocument or XElement or whatnot.

Categories