2

我有一种尝试获取网页的方法。我想尝试多次获取它,所以我围绕它构建了一个包装器以重试几次。在调用的方法中,我捕获然后忽略返回 null 的异常。因此,在第一次尝试后会发生重试。这是被调用的方法:

internal static async Task<string> WebClientAsync(string URI, NetworkCredential Creds = null, Dictionary.FantasySite Site = Dictionary.FantasySite.Other)
{
    if (Creds == null)
    {
        try
        {   //attempt to get the web page
            HttpClient client = new HttpClient(); //create client
            HttpResponseMessage response = await client.GetAsync(URI);  //get response
            response.EnsureSuccessStatusCode(); //ensure the response is good (or throw Exception)
            return await response.Content.ReadAsStringAsync();  //return the string back
        }
        catch (HttpRequestException)
        {
            //MessageBox.Show(string.Format("\nHttpRequestException Caught!\nMessage :{0} for URI {1}.", e.Message, URI));
            return null;  //Catch the exception because we wrapped this and are trying again (null is the indicator it didn't work)
        }
        catch (Exception)
        {
            //MessageBox.Show(string.Format("\nException Caught!\nMessage :{0} for URI {1}.", e.Message, URI)); //TODO - THis hasn't happened, but remove it for production
            return null; //Catch the exception because we wrapped this and are trying again  (null is the indicator it didn't work)
        }
    }

}

如果在所有重试后仍然失败,那么我想抛出异常,但因为我把它扔掉了,我不能。这里是调用方法。

internal static async Task<string> WebClientRetryAsync(string URI, NetworkCredential Creds = null, Dictionary.FantasySite Site = Dictionary.FantasySite.Other)
{
    string webPage = null;
    for (int i = 0; i < Dictionary.WEB_PAGE_ATTEMPTS; i++)  //Attempt to get the webpage x times
    {
        System.Diagnostics.Debug.Print(string.Format("WebClientRetryAsync attempt {0} for {1}", i + 1, URI));
        //wait some time before retrying just in case we are too busy
        //Start wait at 0 for first time and multiply with each successive failure to slow down process by multiplying by i squared
        Thread.Sleep(Wait.Next(i * i * Dictionary.RETRY_WAIT_MS));
        webPage = await WebClientAsync(URI, Creds, Site);
        if (webPage != null) { break; } //don't attempt again if success
    }
    /*TODO - If webPage is null we didn't have success and need to throw an exception.  
     * This is done in the calls to this method and should be done here, move code over */
    return webPage;
}

有人可以建议这是否是一种不好的方法,以及如何在失败太多次后重构代码以引发异常?我是否应该将异常传递给调用方法并忽略它,直到重试用完?

4

1 回答 1

3

是的。您不应该丢弃您希望重新抛出的异常。一种可能的方法如下(尝试对当前代码进行最少的修改):

internal static async Task<string> WebClientAsync(string URI, NetworkCredential Creds = null, Dictionary.FantasySite Site = Dictionary.FantasySite.Other)
{
    // If (Creds == null) removed, you must return a task or throw an exception.

    //attempt to get the web page
    HttpClient client = new HttpClient(); //create client
    HttpResponseMessage response = await client.GetAsync(URI);  //get response
    response.EnsureSuccessStatusCode(); //ensure the response is good (or throw Exception)
    return await response.Content.ReadAsStringAsync();  //return the string back
}

internal static async Task<string> WebClientRetryAsync(string URI, NetworkCredential Creds = null, Dictionary.FantasySite Site = Dictionary.FantasySite.Other)
{
    // assumes you have .NET 4.5, otherwise save exception.
    // uses System.Runtime.ExceptionServices;
    ExceptionDispatchInfo exceptionDispatchInfo = null; 

    for (int i = 0; i < Dictionary.WEB_PAGE_ATTEMPTS; i++)  //Attempt to get the webpage x times
    {
        System.Diagnostics.Debug.Print(string.Format("WebClientRetryAsync attempt {0} for {1}", i + 1, URI));
        try
        {
            var webPage = await WebClientAsync(URI, Creds, Site);
            return webPage;
        }
        catch (Exception ex)
        {
            // save exception so it can be rethrown.
            exceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex);
        }
        // Edit: also need to do an async wait (no thread.sleep):
        if (i < Dictionary.WEB_PAGE_ATTEMPTS - 1)
        {
            //wait some time before retrying just in case we are too busy
            //Start wait at 0 for first time and multiply with each successive failure to slow down process by multiplying by i squared
            await Task.Delay(Wait.Next(i * i * Dictionary.RETRY_WAIT_MS));
        }
    }
    Debug.Assert(exceptionDispatchInfo != null); // shouldn't be null if we get here.
    exceptionDispatchInfo.Throw();
}
于 2013-08-30T02:21:57.660 回答