Life has exceptions, and Web Applications are no Exception

I started writing the text of this post as a bug tracker ticket, but I soon realised that the issue I’ll describe is so generic, that I I decided to turn it in a public post instead.

The problem

So, the prolem is this: we have a web application, obviously (nowadays), that consists of client code, which gets data and other services from web API invocations. I’m usually the one developing the server side (God, protect me from end users and Javascript, I can take care of the server side…).

When the API is invoked, the client should check if the returned HTTP status is in the 200 – 299 range, it should not try to process the output data as if the API succeeded.

In particular, failing this check can cause data processing errors that the end user doesn’t see, leaving them with a blank screen and the feeling the UI is broken without knowing why, which also causes not useful error reports (‘the screen is blank’, rather than ‘the error is shown’).

The general fix

For this reason, this problem should be handled with 1) making the client code aware that API can fail and stopping a flow when that happens and 2) having a general top layer exception interceptor (ie, which is triggered when no other flow does a catch exception {...} to try its own recovering), which should interrupt the regular flow and should tell the user something bad has happened.

With the API contribution

Here, the API has some good behaviour to express too: set up an error handler on the web requests, which format a (JSON) nice report to return in place of the expected data (and of course, return a proper HTTP status code).

Not only have most web frameworks helpers to ease that, but a couple of conventional ways have been proposed to report web errors. In my APIs, I’ve adopted a variant of RFC 9457 and if you work with Spring, here it is the reusable handler for that.

So, once you have a behaving API, a client API error handler should exploit the error report that the API returns when HTTP Status != 200 (see details below) to report the generic short title coming from such API report and logging (console.error()) the details, which are included in the same report (for the benefit of developers or advanced users, server developers should make sure sensitive information don’t escape this way).

CORS complications

Another problem that unchecked HTTP status code causes to client developers is the false impression that they have CORS problems. As it is well-known, when some Javascript from a given site tries to get data from another site (as it normally happens in an API call), the browsers block the cross-site request by default, due to security reasons. API developers usually solve this problem by attaching an HTTP header to the API invocation’s output, which tells the browser to disable this restriction (or, in a few cases, it tells them to disable it for a list of known web domains).

However, this can only happen at the API implementation level, before that is reached. Unfortunately, you typically have an upfront generic web server, such as NGINX or Apache, which cannot relax the restriction for all the requests it gets (technically possible, but too insecure, it could be done more selectively, by filtering the URLs about APIs, but too time-consuming). So, if a request yields errors like "404/URL Not found" or "500/ the internal API server is down", it’s the upfront web server that replies to the client, without anyone setting the CORS-disable header.

Downstream this situation, if you try to process a request that has failed this way pretending it didn’t fail, you will see the CORS blocked error, which is not the actual error that happened. In order to know the latter, you need to check the HTTP status code returned from the API invocation and you need to handle an error situation as explained above.

Typically, a web server will return HTML in this case (the default error page), but the client can still: 1) know an error happened 2) check if there is an RFC 9457 error output 2.1) if yes, use the output to report error details 2.2) if no details are available, use the HTTP status code to tell the end user a more generic error message (most programming languages have libraries that map HTTP codes to human-readable descriptions).

Happy coding, even when it’s not happy

In conclusion, since life doesn’t always go as good as we wish and software isn’t infallible, you shouldn’t code as if it wasn’t so, exceptions never happen and you can always assume you have correct output generated by a successful component invocation.

For if you do, sooner or later you’ll inevitably face issues, and worse, you might not see the actual reason why you’re facing them.

Bonus: my format for reporting exceptions from Java web API.

As said above, I’ve a Java component that I attach to Spring applications, so that it catches all unhandled exceptions, does some automation to establish the right HTTP Status output and yields error reports in RFC 9457 format. For the latter, I’ve adopted a customised variant:

{
   // The FQN of the Java exception's class (top level)
   "type": "org.springframework.http.converter.HttpMessageNotReadableException",

   // Generic message
   "title": "Failed to read request",

   // HTTP status (returned the usual HTTP way too)
   "status": 400,

   // Detailed message (ie, the Java's exception message)
   "detail": "JSON parse error: Unexpected end-of-input: expected close marker for Object (start marker at ...",

   // the endpoint, as per RFC-9457
   "instance": "/v1/graph-info/elements",

   // The Java stack trace (omitted by default, see the configuration options)
   "trace": "org.springframework.http.converter.HttpMessageNotReadableException: ...\n
     at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType..."

}
Click to rate this post!
[Total: 0 Average: 0]

Written by

I'm the owner of this personal site. I'm a geek, I'm specialised in hacking software to deal with heaps of data, especially life science data. I'm interested in data standards, open culture (open source, open data, anything about openness and knowledge sharing), IT and society, and more. Further info here.

Leave a Reply

Your email address will not be published. Required fields are marked *