CodeOath
← All posts
Architecture & Patterns55 min total · 13 parts

Middleware Pipelines Compared: ASP.NET Core, Express.js, and Django

Contents — Part 9 of 13: Error-Handling Middleware
Part 9 of 13 · ~1 min

Error-Handling Middleware

All three frameworks converge on the same idea for errors — a special layer (or hook) that only runs when something goes wrong — but the mechanism used to route control there differs meaningfully:

// ASP.NET Core — a normal try/catch around await next(), or the built-in helper:
app.UseExceptionHandler("/error");
// Express — a synchronous throw inside a normal handler is caught automatically;
// an async rejection must be forwarded explicitly via next(err)
app.get("/orders/:id", async (req, res, next) => {
  try {
    const order = await fetchOrder(req.params.id);
    res.json(order);
  } catch (err) {
    next(err);  // required — Express does NOT auto-catch promise rejections
  }
});
# Django — process_exception, or letting DEBUG=False render a generic 500 page
class ErrorLoggingMiddleware:
    def process_exception(self, request, exception):
        log.exception("Unhandled error")
        return None  # None means "keep propagating" — don't swallow it here

The Express case is worth calling out specifically because it's a well-known gotcha: Express's automatic error catching only covers synchronous throws inside a plain handler — a rejected Promise inside an async route handler is not automatically forwarded to error-handling middleware unless the code explicitly catches it and calls next(err). An unhandled rejection there can crash the process or hang the request, depending on the Node/Express version, rather than producing a clean 500 response.