I'm trying to figure out what to do with some errors in Unity when I call an API and how to propogate it up to the user interface - where and how to handle things. I've built aspnet APIs but there I'd normally use some error handling middleware to keep my controllers clean.
Let say we have some code like this (I'm using controller / repository language cos that's what I know).
A UI button fires an event like OnLoginButtonPressed.
An AuthController class reacts to the event by calling it's login method and then doing some logic when the response comes through, as follows:
public async void Login(LoginModel input) { var result = await AuthRepo.instance.Login(input); app.token = result; EventService.OnSuccessfulLogin(); }
The Auth.Repo calls the API and tries to return a Token class (just a wrapper around a JWT string)
public async Task<Token> Login(LoginModel input) { string json = JsonConvert.SerializeObject(input); var request = UnityWebRequest.Post(app.baseURL "authentication/login", json); request.SetRequestHeader("Content-Type", "application/json"); request.SendWebRequest(); while (!request.isDone) { await Task.Yield(); } if (request.result == UnityWebRequest.Result.Success) { Token token = JsonConvert.DeserializeObject<Token>(request.downloadHandler.text); return token; } else { throw } }
So that's without exception handling. So I want to try to let the user know if there is a connection error, or they have put invalid details etc... I'm guessing I'm supposed add some logic into the AuthRepo such as:
if (request.result == UnityWebRequest.Result.Success)
{
Token token = JsonConvert.DeserializeObject<Token>(request.downloadHandler.text);
return token;
}
else if (request.result== UnityWebRequest.Result.ConnectionError)
{
throw new ConnectionException(request.error);
}
else if (request.result == UnityWebRequest.Result.DataProcessingError)
{
throw new BadRequestException(request.error);
}
else
{
throw new System.Exception(request.error);
}
This seems like a lot of code, and would end up in every method in every repo (unless I pull it out into some helper method?).... anyway, and then in the controller I would do something like:
try {
var result = await AuthRepo.instance.Login(input);
app.token = result;
EventService.OnSuccessfulLogin();
}
catch (ConnectionException ex)
{
EventService.OnConnectionError(ex.Message);
//some UI object would listen for this event and show the connection error message.
}
catch (BadRequestException ex)
{
EventService.LoginFailedError(ex.Message);
}
finally
{
EventService.UnknownError(ex.Message);
}
Is this completely down the wrong path? Seems like the code is just gonna get swamped with exception handling, or is this the correct way?
I've worked through a few YouTube videos that seem to suggest this is right, but they don't really show my use case (talking to APIs) so I'm just trying to be sure.
CodePudding user response:
because UnityWebRequest.Result
is an enum, you can start by using a switch statement here. Not only is this cleaner, it performs better too.
Another thing you can do is create an abstract class (e.g. APIException
) and make that responsible for creating the correct exception instances, by giving it some static method like APIException FromUWRResult(UnityWebRequest.Result result)
.
Handling the exceptions can be done in APIException
too. Give it an abstract method Handle()
and implement accordingly in each of the deriving classes.
Now your code would look like this:
var ex = APIException.FromUWRResult(request.result);
if(ex != null) {
throw ex;
}
...
catch(APIException ex) {
ex.Handle();
}