Error handling

Reference | Web Services


Granitic provides components, patterns and interfaces that allow you to provide a consistent way of communicating errors to your web service clients. The core types involved in Granitic error handling are the ws.CategorisedError and ws.ServiceErrors.

These two types are used by both Granitic itself and your application logic.

Categorised errors

A ws.CategorisedError has four elements:

  • A category to which the error belongs
  • A unique text code to identify the error
  • A human readable message
  • A field name to which the error relates (optional)

Categories

Granitic has a system of categorisation for errors encountered during web service requests. The categories are:

  • Client - the web services client has sent a bad or invalid request
  • Logic - the request was valid, but executing it would violate some form of logical constraint
  • Security - the caller is not allowed to make the request
  • Unexpected - an internal problem (e.g. connectivity outage) was encountered

These categories are programmatically represented by constants in the ws package

The purpose of the categories is to:

  1. Allow callers to understand where the ‘fault’ lies for the error
  2. To allow Granitic to choose an appropriate HTTP response code for the web service request

Codes

Associating a code with an error allows callers to recognise and handle types of errors without having to parse a human readable message. For example, you may decide to use the code DUPE_EMAIL for the an error raised because an email address is already being used by another user.

If you choose to use the ServiceErrorManager facility to manage your error messages in configuration files, the code is used to find the message associated with the error.

Message

The message component of the error allows you to provide a more detailed explanations of why the error occurred in a human readable form. You can specifiy these messages in code where an error is raised, or use the
ServiceErrorManager facility to manage your error messages in configuration.

Fields

When an error specifically relates to a data field provided by the caller, you can (optionally) specify the name of that field.

Service Errors

Your web service’s handler.WsHandler creates a data structure of type ws.ServiceErrors to collect errors. It is made available to your application logic via the Errors field of the ws.Response object passed to your code.

It provides an interface for recording errors as they are encountered. For example:

func (l *Logic) ProcessPayload(ctx context.Context, req *ws.Request, res *ws.Response, t *SomeTarget) {

  se := res.Errors
	
  se.AddNewError(ws.Client, "NO_NAME", "You must provide a name")
}

Predefined errors

Using the AddPredefinedError method on ws.ServiceErrors requires you to have enabled the ServiceErrorManager facility

Representing errors in HTTP responses

If no errors are encountered while processing a request, Granitic will set the HTTP status to 200 Okay and use the Body field of the ws.Response as the HTTP response body.

If errors have been found, Granitic will examine the categories of the errors to determine the correct response code to use and replace the body with a data structure specific for communicating errors. For example, if you are using the default JSONWs facility with automatic validation enabled, a web service caller might see something like:

{
  "ByField":{
    "Name":[
     {
       "Code":"C-INVALID_ARTIST",
       "Message":"Cannot create an artist with the information provided."
     }
    ]
  }
}

The format of error responses is specific to the web services facility, JSON or XML, you have enabled and is documented there along with instructions on how to change the behaviour to match your project standards.

HTTP status codes

Granitic examines the ws.Response at the end of request processing and chooses the most appropriate HTTP status code according to the following rules:

  1. If the Response.HTTPStatus field is non-zero, use that
  2. If the Response.Errors.HTTPStatus field is non-zero, use that

Otherwise, if the Response.Errors structure:

  1. Contains one or more Unexpected errors, use HTTP 500 - Internal server error
  2. Contains an HTTP error, convert that error’s code to a number and use that
  3. Contains one or more Security errors, use HTTP 401 - Unauthorized
  4. Contains one or more Client errors, use HTTP 400 - Bad Request
  5. Contains one or more Logic errors, use HTTP 409 - Conflict

Framework errors

Before Granitic passes control to your logic component any errors encountered while performing the early phases of request processing are recorded using the ws.FrameworkError type.

Generally this process is transparent to your application - errors encountered before your logic are generally due to malformed client requests that cannot be easily recovered from and Granitic will automatically return a HTTP 400 - Bad Request response.

But if, you choose, you can disable automatic handling of these errors by setting the DeferFrameworkErrors field of your handler.WsHandler to false.

Your application logic will then have access to the framework errors found via the FrameworkErrors field of the ws.Request passed to it.

Configuration

The messages that are served to users in those circumstances are defined in configuration. The default values are:

{
  "FrameworkServiceErrors":{
    "Messages": {
      "UnableToParseRequest": ["PARSE","Unable to parse the body of the request. Please check the content you are sending."],
      "QueryTargetNotArray":  ["QUERYBIND", "Multiple values for query parameter %s. Only one value supported"],
      "QueryWrongType": ["QUERYBIND", "Unable to convert the value of query parameter %s to type %s. Value provided was %s"],
      "QueryNoTargetField": ["QUERYBIND", "No field named %s exists to bind query parameter %s into."],
      "PathWrongType": ["PATHBIND", "Unable to convert the value of a path parameter (group %s) to type %s. Please check the format of your request path. Value provided was \"%s\""]
    },
    "HTTPMessages": {
      "401": "Access to this resource requires authorization.",
      "403": "You do not have permission to interact with that resource.",
      "404": "No such resource.",
      "500": "An unexpected error occurred.",
      "503": "The service is too busy to process your request or is temporarily unavailable."
    }
  }
}

You can override these messages as you would any other configuration value.


Next: Identity Access Management

Prev: Application logic