UNPKG

strata

Version:

A modular, streaming HTTP server

151 lines (114 loc) 6.2 kB
/* # Errors JavaScript programs do not typically have very good error reporting or handling. Two of the main problems that users face when trying to report and handle errors properly are: - JavaScript provides the user with little more than a base `Error` object for reporting errors - The prominent use of callbacks in asynchronous programming renders the `try`/`catch` mechanism useless for handling errors most of the time because errors are typically thrown from inside the callback Strata attempts to address both of these issues so that users can more easily build robust servers with excellent error reporting and handling capabilities. ## Error Reporting Strata uses a simple abstraction for errors that makes it simple to build meaningful hierarchies of errors, complete with stack traces. You can inherit from Strata's base error prototype, `strata.Error` to get this extra functionality. var util = require("util"); var strata = require("strata"); function LoginFailedError(message, cause) { message = message || "Login Failed"; strata.Error.call(this, message, cause); } util.inherits(LoginFailedError, strata.Error); The `message` argument to the `strata.Error` constructor serves as that error's message. The `cause` argument is another Error object that was the cause of this error at some lower level. When an Error is created with another error as its cause, the cause is available in the error's `cause` property and stack traces of both are available in the error's `fullStack` property. This allows you to easily trace the origins of an error back to the lowest level error responsible. For example, the `LoginFailedError` demonstrated above could be caused by any number of other errors. An invalid username and/or password or a database connectivity issue are just a few. ## Error Handling If you read through the example code in the previous chapter, you may have noticed that there was an `err` argument provided in the callback to `req.params` that was simply ignored. This style of passing errors as the first argument to a callback is standard practice in node programs. Typically, error handling in an asynchronous environment is a pain point for programmers. The problem is that when you're in a callback you can't simply [throw](https://developer.mozilla.org/en/JavaScript/Reference/Statements/throw) the error because the callback is called out of the original calling context. Thus, any `try`/`catch` mechanism that was used when the caller was called is not able to catch the error when thrown. Since an uncaught error will crash the program, we need a mechanism to handle them gracefully. The solution in Strata is the global error handler, `strata.handleError`. This function takes three arguments: the error, and the environment and callback arguments that were passed to the app. In Strata, the environment and callback represent the context of the request, so even though the calling context is no longer accessible we can still return an intelligent response to the client without bringing the whole server down. The return value of `strata.handleError` **must** always be a boolean. A `true` value means that the callback was used to issue a response to the client so the server may stop processing the request, while `false` means that the error was not fatal and the request may continue processing. By default all errors are considered fatal, so `true` is returned. To properly handle the error from the previous chapter, we could modify the app to look like this: */ var strata = require("strata"); strata.run(function (env, callback) { var req = strata.Request(env); req.params(function (err, params) { // If there was an error and strata.handleError issued a response // based on it, return and stop processing the request. if (err && strata.handleError(err, env, callback)) { return; } var content = JSON.stringify(params); var res = strata.Response(content); res.contentType = "application/json"; res.contentLength = Buffer.byteLength(content); res.send(callback); }); }); /* The `strata.handleError` function handles the error by issuing a 500 Internal Server Error to the client and writing the error to `env.error` (the error stream, see the [SPEC](https://github.com/mjijackson/strata/blob/master/SPEC)). ## Custom Error Handling The `strata.handleError` function should be overridden by users who wish to run custom error handling logic at the global level. Continuing with the example app above, you may wish to serve a 400 response when the error is caused by a client who sent invalid request parameters. After all, it's not the server's fault the client sent bad data. So returning a 500 in this case isn't very helpful. For example, a client may send a `Content-Type` header with a value of `application/json` but with invalid [JSON](http://www.json.org/) in the request body. In this instance, the error that is passed to the `req.params` callback is an instance of `strata.InvalidRequestBodyError`. If you wish to handle all instances of this error with a 400 response, you may override `strata.handleError`. */ // Get a reference to the original error handler. var _handleError = strata.handleError; strata.handleError = function (err, env, callback) { if (err instanceof strata.InvalidRequestBodyError) { callback(400, {}, "Invalid Request Body"); return true; } return _handleError(err, env, callback); }; /* As in previous chapters, you can save the code in the examples above to a file named `app.js` and run it with the `node` executable: $ node app.js Then view the app at [http://localhost:1982/](http://localhost:1982/). To get this app to return a 400 response, send it some bad data. For example, send an `application/json` request with invalid JSON in the request body, like this: $ curl -v -H "Content-Type: application/json" --data "a" http://localhost:1982/ When the JSON parser is unable to parse the request body, it will return an error to the call to `req.params`, which in turn will issue a 400 response using the overridden version of `strata.handleError`. */