Home > Net >  What is the best way to check a url is valid every second (or less), with Task await or ContinueWith
What is the best way to check a url is valid every second (or less), with Task await or ContinueWith

Time:07-08

I'm new to C# .Net and Visual Studio 2022 - What I'm trying to achieve is to have a timer running every second to check that a website url is valid/is up. If the url IS reachable and the current WebView2 is not showing that website, then it should navigate to it. If it's already showing that website, it should do nothing else. If it was showing that website, but now it's no longer valid, the WebView should navigate to my custom error page. If whilst on the custom error page the website becomes available again, it should (re)load the website. In my particular scenario I'm making a webView load localhost (127.0.0.1) for now. I want to continuously check the website is ip, and if it goes down, show custom error, if it comes back, show the website.

Not sure I'm explaining that very well. From the research I have done, I believe I need Task and also await using async method.

Here's my current timer and checkurl code as well as navigtionstarted and navigationcompeted:

private void webView_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
{
    timerCheckRSLCDURL.Enabled = false;
}

private void webView_NavigationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs e)
{
    if (e.IsSuccess)
    {
        Debug.WriteLine("JT:IsSuccess");
        ((Microsoft.Web.WebView2.WinForms.WebView2) sender).ExecuteScriptAsync("document.querySelector('body').style.overflow='hidden'");
    }
    else if (!e.IsSuccess)
    {
        Debug.WriteLine("JT:IsNOTSuccess");
        webView.DefaultBackgroundColor = Color.Blue;
        //webView.CoreWebView2.NavigateToString(Program.htmlString);
    }
    timerCheckRSLCDURL.Enabled = true;
}

private void timerCheckRSLCDURL_Tick(object sender, EventArgs e)
{
    Debug.WriteLine("Timer Fired! Timer.Enabled = "   timerCheckRSLCDURL.Enabled);
    CheckURL(Properties.Settings.Default.URL, Properties.Settings.Default.Port);
}

private async void CheckURL(string url, decimal port)
{
    timerCheckRSLCDURL = false;
    Program.isWebSiteUp = false;
    string webViewURL = BuildURL();
    Debug.WriteLine("Checking URL: "   webViewURL);

    try
    {
        var request = WebRequest.Create(webViewURL);
        request.Method = "HEAD";
        var response = (HttpWebResponse) await Task.Factory.FromAsync < WebResponse > (request.BeginGetResponse, request.EndGetResponse, null);
        if (response.StatusCode == HttpStatusCode.OK)
        {
            Program.isWebSiteUp = true;
        }
    }
    catch (System.Net.WebException exception)
    {
        Debug.WriteLine("WebException: "   exception.Message);
        if (exception.Message.Contains("(401) Unauthorized"))
        {
            Program.isWebSiteUp = false;
        }
        else
        {
            Program.isWebSiteUp = false;
        } // This little block is unfinished atm as it doesn't really affect me right now
    }
    catch (Exception exception)
    {
        Debug.WriteLine("Exception: "   exception.Message);
        Program.isWebSiteUp = false;
    }

    if (Program.isWebSiteUp == true && webView.Source.ToString().Equals("about:blank"))
    {
        Debug.WriteLine("JT:1");
        Debug.WriteLine("isWebSiteUp = true, webView.Source = about:blank");
        webView.CoreWebView2.Navigate(webViewURL);
    }
    else if (Program.isWebSiteUp == true && !webView.Source.ToString().Equals(webViewURL))
    {
        Debug.WriteLine("JT:2");
        Debug.WriteLine("isWebSiteUp = true\nwebView.Source = "   webView.Source.ToString()   "\nwebViewURL = "   webViewURL   "\nWebView Source == webViewURL: "   webView.Source.ToString().Equals(webViewURL)   "\n");
        webView.CoreWebView2.Navigate(webViewURL);
    }
    else if (Program.isWebSiteUp == false && !webView.Source.ToString().Equals("about:blank"))
    {
        Debug.WriteLine("JT:3");
        Debug.WriteLine("This SHOULD be reloading the BSOD page!");
        webView.CoreWebView2.NavigateToString(Program.htmlString);
    }
}

private string BuildURL()
{
    string webViewURL;
    string stringURL = Properties.Settings.Default.URL;
    string stringPort = Properties.Settings.Default.Port.ToString();
    string stringURLPORT = $ "{stringURL}:{stringPort}";
    if (stringPort.Equals("80"))
    {
        webViewURL = stringURL;
    }
    else
    {
        webViewURL = stringURLPORT;
    }
    if (!webViewURL.EndsWith("/"))
    {
        webViewURL  = "/";
    }
    //For now, the URL will always be at root, so don't need to worry about accidentally
    //making an invalid url like http://example.com/subfolder/:port 
    //although potentially will need to address this at a later stage

    Debug.WriteLine("BuildURL returns: "   webViewURL);

    return webViewURL;
}

So the timer is fired every 1000ms (1 second) because I need to actively check the URL is still alive. I think the way I'm controlling the timer is wrong - and I imagine there's a better way of doing it, but what I want to do is this...

  • Check website URL every 1 second
  • To avoid repeating the same async task, I'm trying to disable the timer so it does not fire a second time whilst the async checkurl is running
  • Once the async/await task of checking the url has finished, the timer should be re-enabled to continue monitoring is the website url is still up
  • If the website is down, it should show my custom error page (referred to as BSOD) which is some super basic html loaded from resources and 'stored' in Program.htmlString
  • if the the website is down, and the webview is already showing the BSOD, the webview should do nothing. The timer should continue to monitor the URL.
  • if the website is up and the webview is showing the BSOD, then it should navigate to the checked url that is up. If the website is up, and the webview is already showing the website, then the webview should do nothing. The timer should continue to monitor the URL.

From other research, I'm aware I shouldn't be using private async void - eg shouldn't be using it as a void. But I've not yet figured out / understood the correct way to do this

In the Immediate Window, it appears that webView_NavigationCompleted is being fired twice (or sometimes even a few times) instantly as the immediate window output will show JT:IsSuccess or JT:IsNOTSuccess a few times repeated in quick succession. Is that normal? I'm assuming something isn't correct there.

The main problem appears to be due to the timer being only 1 second. If I change the timer to fire every 30 seconds for example, it seems to work ok, but when it's every second (I may even need it less than that at some point) it's not really working as expected. Sometimes the BSOD doesn't load at all for example, as well as the webView_NavigationCompleted being fire multiple times in quick succession etc.

Could someone pretty please help me make this code better and correct. I've searched countless websites etc and whilst there is some good info, some of it seems overwhelming / too technical so to speak. I had to lookup what "antecedent" meant earlier as it's a completely new word to me! :facepalm:

Many thanks inadvance

CodePudding user response:

This answer will focus on the Task timer loop to answer the specific part of your question "check a url is valid every second". There are lots of answers about how to perform the actual Ping (like ping result x 3

Example 404:

No such host is known

  • Related