Web API Error Management – Part 1
Despite the claims of our previous and accidentally released article from last Sunday, this will be our first “how to” article related to the SRP HTTP Framework. We’ll look at the different methods and circumstances for handling errors in your web APIs. Part 1 will focus on the mechanics of error management within OECGI based web applications. In Part 2, we’ll consider best practice implementations of each error management option. Because the SRP HTTP Framework is based upon OECGI technology, the underlying communication layer between OEngineServer and OpenEngine remains the same. Therefore, most of this article will apply to both RESTful APIs created in our framework as well as traditional INET/O4W APIs created with the out of the box toolkit.
Types of Error Conditions
Let’s first review the types of error conditions that can occur within your web service code:
- Runtime Errors – Just like our OpenInsight desktop applications, our BASIC+ web services can encounter runtime errors such as the nefarious VNAV.
- Status Errors – Many system stored procedures (SSPs) rely upon Set_Status() to throw an unexpected error condition. For instance, a failed RList() call will produce such an error. Developers can also use Set_Status() in their own procedures for error management.
- API Errors – These are developer-defined failures within the API. For instance, if the API is expecting to read a database row but that row does not exist, this could be considered an API error.
Runtime and status errors can both affect how OpenEngine continues to process code whereas API errors will only affect what the HTTP Response will contain (e.g., if a requested database row is missing, the developer might return a 404 status code). Although runtime and status errors have different degrees of severity, they can have a similar impact on OpenEngine. Therefore, unless a distinction between these two is necessary, they will be discussed collectively as system errors in this article.
Communication in the Web Stack
It will be helpful to get a high-level view of how the core components in our web stack communicate with each other. This graphic helps to see the basic flow of incoming requests (blue arrows) and outgoing responses (green arrows):
The Dispatcher (aka Controller) is the routine that is configured in the ProcedureName Registry value for the OECGI. HTTP_MCP is the default dispatcher for the SRP HTTP Framework and RUN_OECGI_REQUEST is used by traditional INET/O4W systems. Dispatch routines accept two arguments: Request and ProcErr. When a normal HTTP Request comes into the OECGI, Request is populated while ProcErr is empty and these are sent into the dispatch routine via OEngineServer. The Dispatcher then calls other routines as needed. If everything executes without any system errors, a valid HTTP Response is sent back. This is the ideal flow: a single round-trip.
Unresolved system errors, however, interrupt this normal flow. When OEngineServer gets the response from OpenEngine it checks to see if the termination was normal or if there was an error. Unless a Debugger Intercept routine is enabled, all runtime errors will force OpenEngine to have an abnormal termination and OEngineServer will see this as an error condition. Status errors, however, will not abort OpenEngine, but they will still return an error condition to OEngineServer. Therefore, under most conditions, runtime and status errors will be treated the same. OEngineServer then packages the entire response from OpenEngine along with its own status flag (i.e., 0 for OK, or 1 for error) and sends this up to the OECGI.
If everything is OK, then the OECGI continues to send the package (HTTP Response) back to the client that made the request (i.e., the aforementioned single round-trip). If there is an error, then the OECGI sends this data back to the Dispatcher. However, in this case the data comes in through the ProcErr argument. Dispatch routines, therefore, must be programmed to check ProcErr for data and handle the error information in a suitable manner. Many developers just format the information into a standard response so that the client can be advised of the error. Thus, this process generates a U-turn at the OECGI and creates one more round trip before the response is sent back to the source.
Handling System Errors
When the ProcErr argument is populated, the RUN_OECGI_REQUEST dispatcher will look into the SYSENV\CFG_INET configuration record to identify the handler. The default handler routine is INET_ABORTED, although this can be easily changed using the RTI_CFG_INET form (which is also accessible through Tools > INET Configuration menu in the newer IDE).
In the next release of the SRP HTTP Framework, the HTTP_MCP dispatcher will rely upon a new service, GetAbortedService, to retrieve the handler. A default handler routine, HTTP_Aborted_Service, will also be included in the next product release. To make configuration easier, the NDW_HTTP_FRAMEWORK_SETUP form has been updated accordingly:
The content of the ProcErr argument will depend upon the nature of the error. A runtime error will pass its error text with replaceable parameters already populated (e.g., “ENG0010: MY_ROUTINE, line 100. Variable has not been assigned a value”). A status error will contain whatever the SSP or developer passed into the Set_Status() function (e.g., an RList() statement that refers to a missing BALANCE_DUE dictionary column would return, “BALANCE_DUE is an invalid dictionary item”).
Developers might prefer to handle runtime errors separately using a Debugger Intercept routine. Reasons for this will be saved for the next article. However, it is important to know that Debugger Intercepts will get executed before OpenEngine terminates. They also clear the error condition, which prevents OECGI from being aware of the runtime error and thus calling the dispatch routine again with ProcErr populated. If the runtime error is severe enough, such as a VNAV, the OEngineServer connection is irrevocably severed. This causes the web server (e.g., IIS) to timeout and return a default 500 status error page. Some runtime errors, like Non-numeric data when numeric required, might allow the procedure to execute to completion. Thus, despite encountering a system error, an HTTP Response will still return in a single round-trip.
Handling API Errors
An API error implies that the developer is still in control and either there was no runtime error or it was due to a status error that got cleared (i.e., Set_Status(0)) before OpenEngine terminated. Applications based on RUN_OECGI_REQUEST might use INET_FINALIZE to handle any default API error responses. SRP HTTP Framework does not have a service that is equivalent to INET_FINALIZE. However, developers can add their own default handling if desired. The easiest way to do this would be to update the GetResponse service to check for specific status codes (such as anything in the 500 series) and then add boiler plate content to the existing HTTP Response. The main purpose of properly handling API errors is to send back a well formed HTTP Response so that the client can make an informed decision on how to proceed.
This concludes our summary of how error handling works with OECGI applications. In our next article we will look at the pros and cons of using each handler and offer some guidance on getting the best results.