'How should I report errors from within an api wrapper?
I'm writing an API wrapper for an external API. How do I let the consumers know about specific errors that occur in wrapper?
If the consumer is supporting multiple languages, and the wrapper doesn't, I think it would be bad for me to send error-messages in just English text.
At first, to overcome this, I thought of using integer error codes. But, with this, I can only indicate type of error, and will not be able to send back some helpful data.
Now, I am thinking about using Exceptions or custom error objects. Either throw them directly or wrap them in a Result object like Result.failure(exception). But, I am not sure if I should do this. Here's what I will try to do:
class WrapperException(val error: WrapperError): Exception()
or
open class WrapperException: Exception()
class SessionCreationFailedException: WrapperException()
class InvalidRequestTokenException: WrapperException()
class RequestTokenExpiredException: WrapperException()
class SessionRevokedException(val failedSession: String): WrapperException()
class UnexpectedException: WrapperException()
Extending from WrapperException doesn't make sense because there might be exceptions due to code or other reasons like NullPointerException, SocketTimeoutException, etc.
At the end, the consumers will still have to do something like
when (exception) {
is ExceptionA -> handleExceptionA()
is ExceptionB -> handleExceptionB()
else -> somethingWentWrong()
}
I think I shouldn't try to categorize exceptions with WrapperException to evade else statement. And, for consumers to implement error-handling like this, they must know what exceptions a function can throw (some documentation might help here).
It, also, makes me feel that the wrapper functions will become unpredictable. In future updates, consumer might not expect ExceptionZ to appear, but technically, it can. So, maybe updating documentation and changelogs is most I can do here.
Should I avoid sending an error message? I am confused. Please help me understand how I can make it useful for consumers.
Solution 1:[1]
I think sealed classesinterfaces are a better choice than Exceptions since Kotlin doesn't support checked Exceptions. Sealed classes solve some of the issues with checked exceptions that drove the Kotlin designers to choose not to support them in the first place, while not sacrificing code safety (risk of forgetting to catch an exception you need to catch).
- Sealed classes don't create an unnecessary and expensive stack trace when instantiated.
- Each child of the sealed class/interface can wrap whatever kind of data it wants.
- It's impossible to forget to check for or handle an error. Each call in the stack can choose to either unwrap the result and handle the possible error, or just pass the sealed result up the stack.
In this particular case, I might break all the error types out into an enum so there are a bunch of sealed children that might change over time and mess up when statements when you add new errors. In most cases, you won't really need to manually handle each type of error in a unique way. Your enum class could have a property that holds a key/ID for retrieving a user-readable String. Here, I am providing an errorData property as an optional payload of extra data.
sealed interface MyResult<T> {
data class Success<T>(val result: T): MyResult<T>
data class Failure(val error: MyError, val errorData: String = ""): MyResult<Nothing>
}
enum class MyError {
SessionCreationFailed,
InvalidRequestToken,
RequestTokenExpired,
SessionRevoked,
UnexpectedException
}
If it gets more complicated than that with the types of things you want to attach to the error cases, you can have an intermediate interface that is a parent of all the failure types. Then when statements that consume the result don't need to necessarily iterate all the different failure types.
sealed interface MyResult<T> {
data class Success<T>(val result: T): MyResult<T>
interface Failure: MyResult<Nothing>
object SessionCreationFailed: Failure
object InvalidRequestToken: Failure
object RequestTokenExpired: Failure
data class SessionRevoked(val failedSession: String): Failure
object UnexpectedException: Failure
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Tenfour04 |
