0

Is there a way of correctly handling WebExceptions within a Data Access Layer?

Below is a method SendReceive within our DAL used to communicate with our remote server, if there is a communication issue, such as endpoint being inaccessible and therefore no data can be retrieved, I would like the user to be redirected to a View, informing the user to please try again later.

private static TResult SendReceive<TResult, TPayLoad>(string method, string route, TPayLoad payload, bool post, bool authentication, string hashedPassword)
{
    var subject = "WebApplication1 - " + method + " Error";

    using (var webClient = new WebClient())
    {
        try
        {
            var uri = new Uri("http://ourdomain/ourwebapicontroller/" + route);

            webClient.Headers[HttpRequestHeader.ContentType] = "application/json";

            if (authentication)
            {
                var hashedPasswordAsBytes = Encoding.UTF8.GetBytes(hashedPassword);

                webClient.Headers.Add(HttpRequestHeader.Authorization, "Basic " + Convert.ToBase64String(hashedPasswordAsBytes));
            }

            var response = post ? webClient.UploadString(uri, JsonConvert.SerializeObject(payload)) : webClient.DownloadString(uri);

            var parsedResponse = JsonConvert.DeserializeObject<TResult>(response);

            return parsedResponse;
        }
        catch (WebException webException)
        {
            SendEmail(subject, MvcApplication.To, MvcApplication.From, "<p>WebException [" + webException.Message + "]</p>");

            // Issue with endpoint 
        }
        catch (Exception exception)
        {
            SendEmail(subject, MvcApplication.To, MvcApplication.From, "<p>Exception [" + exception.Message + "]</p>");
        }
    }

    return default(TResult);
}

public Models.WebApplication1.Test GetTest(int id)
{
    return SendReceive<Models.WebApplication1.Test, int?>("GetTest", "get-test/" + id, null, false, false, null);
}

public int SetTest(Models.WebApplication1.Test test)
{
    return SendReceive<int, Models.WebApplication1.Test>("SetTest", "set-test", test, true, false, null);
}    

As the DAL is referenced from a Controller I don't believe it is possible to use throw new HttpException(), this can however be handled like so:

public ViewResult Test(int id)
{
    var test = Dal.GetTest(id);

    if (test == null)
    {
        throw new HttpException(404, "Please try again later.");
    }

    return View(test);
}

Would prefer to handle the communication issue within SendReceive as opposed to handling at Controller level for each method referencing SendReceive.

iggyweb
  • 2,373
  • 12
  • 47
  • 77

1 Answers1

0

Everything depends on what you mean by "handle" and even "exception."

Controller
Within the controller, what do you want to do if the client requests something that doesn't exist? A 404 is a good response. But what if the DAL throws an exception? Would it make sense to return the exact same result to the client? A 500 error which tells the client something went wrong might make more sense.

That mismatch is indicated here:

throw new HttpException(404, "Please try again later.");

If the request threw an exception (for any reason, including the DAL) then returning a 500 error with "try again later" makes sense. You're communicating clearly that the problem is on your end. Sorry, hopefully it won't happen again, and if does we're working on it.

If the client requested something that doesn't exist then that may or may not ever change. Should they try again later? Why? Maybe what they've requested will never be found. That's also not an exception. Someone asking for something that doesn't exist and getting nothing means that your application is working correctly. The 404 tells them that our application is working - we just don't have what they want.

Based on that, bubbling up an actual exception to the controller probably makes sense. The DAL doesn't know about the controller or even a website. It's not in a good position to know whether or not the caller should know that there was an exception.

DAL "Handling" an exception can mean different things. (I'll leave out my opinion about which is right because it's not relevant.)

If your DAL throws an exception, you can do a few things. Some are maybe better than others, but again, that depends on opinion and needs. - Do nothing. Let the exception bubble up. - Log it and rethrow it. - Log it then wrap it in another exception that provides some context, and throw the new exception. (Whether to wrap an exception or not is a whole discussion.)

Some would say that "handling" an exception is something different that involves somehow reacting to the exception in a way that solves a problem, something we're less likely to do. For example, if our application retrieves a daily Chuck Norris joke from an API but it throws an exception, we might log it so we know something went wrong but then replace it with a backup Chuck Norris joke.

The most important thing I wouldn't do is "hide" the exception so that, to the caller, an exception and "nothing found" look the same. If something has gone wrong, the controller needs to know that - even if it doesn't understand the specifics - so it (not the DAL) - can determine what should be communicated to the caller.

The relationship between the controller and the DAL is similar to that between the browser client and the controller. If it's not just working, we communicate that. If there's no data, we communicate that.


I don't recommend putting writing code in the DAL that sends an email. That's very specific, and it couples all of your code to that decision and possibly to an implementation of sending mail.

An alternative is defining an interface like this:

public interface ILog
{
    void LogException(Exception ex);
    void LogMessage(string message);
}

...and injecting into the DAL class. When an exception occurs, call _log.LogException(ex);. Your DAL doesn't know what the implementation is. You could log it or even send an email if you want to.

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • Thank you for taking to take the timeout to respond. I've taken the bubble up approach, by throwing a CustomException, capturing the WebException, Method and StatusCode, the ErrorController then parses the CustomException to relay the appropriate message and View. – iggyweb May 17 '19 at 14:13