Skip to content

HTTP exceptions

An exception that extends IHttpException turns into an HTTP error answer.

You can throw it or return it from a middleware or a controller.

IHttpException is in packages/dox-annotation/lib/src/core_annotation.dart:

abstract class IHttpException {
  int code = 500;
  String errorCode = 'server_error';
  dynamic message = 'Server Error';

  dynamic toResponse() {
    if (message is Map) {
      return message;
    }
    return <String, dynamic>{'code': errorCode, 'message': message.toString()};
  }
}

The classes

Most classes take the named parameters message, errorCode and code. The table shows the defaults. message is a String or a Map, like NotFoundHttpException(message: {'error': 'not_found'}).

Class Package Status code errorCode Default message Dox throws it when
BadRequestException dox_core 400 bad_request Bad Request the body can not be read, like JSON that is not valid
UnAuthorizedException dox_auth 401 unauthorized Authentication failed AuthMiddleware finds no valid token
ForbiddenHttpException dox_core 403 forbidden Forbidden never by itself; for your own use
NotFoundHttpException dox_core 404 not_found Not Found no route fits the path, a typed path param does not fit ({id} is not an int), or a route model binding finds no row
MethodNotAllowedHttpException dox_core 405 method_not_allowed Method Not Allowed the path has routes, but not for this method. The Allow header lists the methods.
PayloadTooLargeException dox_core 413 payload_too_large Payload Too Large the body is bigger than maxBodySize or maxMultipartSize
UnsupportedMediaTypeException dox_core 415 unsupported_media_type Unsupported Media Type a DTO reads a body that is not JSON and not form data
TokenMismatchException dox_core 419 csrf_token_mismatch CSRF token mismatch CsrfMiddleware finds no CSRF token, or a wrong one
ValidationException dox_core 422 validation_failed {} (a Map) a DTO or req.validate() finds bad input
TooManyRequestsException dox_core 429 too_many_requests Too Many Requests a RateLimit middleware sees too many requests
InternalErrorException dox_core 500 server_error Server Error never by itself; for your own use
QueryException dox_core 500 sql_query_error Error in sql query never by itself; for your own use

Source files: packages/dox-core/lib/exception/*.dart and lib/src/auth_exception.dart in dartondox/dox-auth.

What the client gets

responseDataHandler in packages/dox-core/lib/http/http_response_handler.dart sends the answer:

  1. The status is set to code.
  2. The body is toResponse().
  3. The body is sent as JSON with Content-Type: application/json.

A text message gives a JSON body with the errorCode and the message:

throw NotFoundHttpException(message: 'Blog not found');
HTTP/1.1 404 Not Found
content-type: application/json

{"code":"not_found","message":"Blog not found"}

A Map message is sent as it is, with no code key, and with the status of the exception:

throw NotFoundHttpException(message: <String, String>{'error': 'not_found'});
// 404 {"error":"not_found"}

ValidationException always has a map, one entry per field:

HTTP/1.1 422 Unprocessable Entity
content-type: application/json

{"email":"The email is not a valid email","name":"The name is required"}

A request for a path that has no route answers like a NotFoundHttpException with the message <METHOD> <path> not found: {"code":"not_found","message":"GET /nothing not found"}. When the path has routes for other methods, the answer is 405 with an Allow header, like Allow: GET, POST, HEAD. Your responseHandler gets it too, so you can change its shape.

To send your own JSON shape, make your own exception class (see below) and override toResponse().

Other errors: any Exception or Error that is not an IHttpException gets 500 with {"code": "server_error", "message": "Server Error"}. When APP_ENV is development or local, the body is error.toString() as text instead. It is also passed to the report method of the exception handler. An IHttpException with a 4xx code is not reported. One with a 5xx code is.

A ValidationException on a request with a session (SessionMiddleware) that does not want JSON, like a form post, is not sent as JSON. Dox saves the errors and the old input in the session and redirects back.

A ValidationException from dox_validation (thrown by schema.parse() in a controller) is turned into the 422 ValidationException of dox_core with the same errors.

Any status: abort and HttpStatusException

abort(code) stops the request and answers with that status. It throws an HttpStatusException.

if (post.userId != req.auth?.id()) abort(403);
abort(404, 'No post with this id');
throw HttpStatusException(503, headers: {'retry-after': '60'});
HTTP/1.1 403 Forbidden
content-type: application/json

{"code":"forbidden","message":"Forbidden"}

Common codes get a default errorCode and message, like forbidden / Forbidden for 403. Other codes get http_error / Error. The headers are sent with the answer.

Make your own

Extend IHttpException and set the three fields in the constructor:

import 'dart:io';

import 'package:dox_core/dox_core.dart';

class PaymentRequiredException extends IHttpException {
  PaymentRequiredException({
    dynamic message = 'Please pay',
    String errorCode = 'payment_required',
    int code = HttpStatus.paymentRequired,
  }) {
    super.code = code;
    super.errorCode = errorCode;
    super.message = message;
  }
}

Override toResponse() to give every error of your app the same JSON shape.

More examples are on the Response page.