There have been some really good, helpful posts on this already:
https://community.intersystems.com/post/robust-error-handling-and-cleanu...
https://community.intersystems.com/post/registering-new-error-code-and-e...
https://community.intersystems.com/post/objectscript-error-handling-snip...
https://community.intersystems.com/post/can-you-have-custom-status-objec...
I'm trying to put this all together and determine what is the best convention for handling errors, logging them and reporting them to the user as they navigate a web application or command line program.
In my own evolution as a ObjectScript developer, I've tried to be more consistent for class methods that return %Status by
- Starting each method with setting st=$$$OK
- Using a try/catch block
- Check if {somethingsWrongOrNotRight} $$$ThrowStatus($$$ERROR($$$GeneralError,"My custom error message."))
to throw the error handling into the catch block
- Log the error to our error table in the catch block using $System.Status.GetErrorText(thrownError.AsStatus())
to get the error message as defined in the bullet above and then s st=thrownError.AsStatus()
- End the method with q st
I don't know if I modeled it quite right, but I took this from studying method implications of InterSystems library methods in various classes. It works quite well especially using backend processes. However, I am continually challenged on the best ways to get the correct error message to the client. A few thoughts:
- Often the error message I am reporting in the audit/error log is not the one I want to display to the client
- Sometimes, especially if implementing an API, I want to handle or consider the status code of the HTTP response.
- We work with CSP pages, REST services (building our own and implementing others), and command line applications.
- Considering the articles and advice above, what's the best way to generate custom errors that we can log to the audit log but then also format 'happy text' that we may need to show the user but also not go through the process of creating a whole new library of custom errors for tons of little validation things along the way.
A use case:
We are implementing an API that tends to have intermittent availability errors and we also call it using a session token that expires. So if we call the API and it's a 502, we just want to stop the presses and try again later. If we call it and it's a 403, we want to refresh the token and try again. In the implementation method I will $$$ThrowStatus($$$ERROR($$$GeneralError,HTTPSRequest.HttpResponse.StatusCode_" "_HTTPSRequest.HttpResponse.Data.Read()))
if the code is not 200 or whatever we expect as successful.
This is good, robust info to add to the audit log so we do that in the catch block, but I still have to return a status. 502 Gateway error is not useful to the client or even to my logic that called it so I find myself doing something like if $$$ISERR(st) { if $SYSTEM.Status.GetErrorText(st)[502 {//handle that} elseif $SYSTEM.Status.GetErrorText(st)[403 {//handle this}}
.
I suppose this works, but it feels hacky to me. I would prefer setting a custom error code so I didn't have to rely on the string analysis to determine what to do. But I don't feel it's necessary to create a whole new custom error code in a library for this one off reason. Further, if this were a method that was returning something to a client now I have an additional step of checking the string and handling the audit but also now crafting a display message to the user "The service is currently unavailable. Please try again later or call technical support."
That $$$ThrowStatus()
macro compiles to ##safeexpression($$macroERROR^%occMsgXML(%literalargs))
and I don't know from looking at that if my message could be an object so that I have something like {"errorCode": "API502", "errorAudit": "Gateway error", "errorDisplay": "The service is currently unavailable. Please try again later or call technical support."}
. I can view the %occStatus
routine but I cannot find the %occMsgXML
routine to examine what I can or can't pass into this. That said, I haven't seen this as a recommended solution anywhere, so I'm sure that's wrong. Does anyone have a good example of how they use a %Status to report an error to a client gracefully?
At the end of the day I think I'm trying to fit square pegs into round holes, so maybe someone can set me straight on some best practices here.