import ballerina/http;
import ballerina/io;

// Header name checked by the request interceptor.
final string check_header = "checkHeader";

// Header value to be set to the request in the request error interceptor.
final string request_check_header_value = "RequestErrorInterceptor";

service class RequestInterceptor {
    *http:RequestInterceptor;

    // This will return a `HeaderNotFoundError` if you do not set this header. 
    // Then, the execution will jump to the nearest `RequestErrorInterceptor`.
    resource function 'default [string... path](http:RequestContext ctx, 
            @http:Header string checkHeader) returns http:NextService|error? {
        io:println("Check Header Value : ", checkHeader);
        return ctx.next();
    }
}

RequestInterceptor requestInterceptor = new;

// A `RequestErrorInterceptor` service class implementation. It allows you to 
// intercept the error that occurred in the request path and handle it accordingly.
// A `RequestErrorInterceptor` service class can have only one resource function.
service class RequestErrorInterceptor {
    *http:RequestErrorInterceptor;

    // The resource function inside a `RequestErrorInterceptor` is only allowed 
    // to have the default method and path. The error occurred in the interceptor
    // execution can be accessed by the mandatory argument : `error`.
    resource function 'default [string... path](error err, http:Request req, 
            http:RequestContext ctx) returns http:NextService|error? {
        // In this case, a header is set to the request, and then, the modified request
        // is dispatched to the target service. Moreover, you can send different 
        // responses according to the error type.
        req.setHeader(check_header, request_check_header_value);
        return ctx.next();
    }
}

// Creates a new `RequestErrorInterceptor`.
RequestErrorInterceptor requestErrorInterceptor = new;


listener http:Listener interceptorListener = new http:Listener(9090, config = {
    // To handle all of the errors in the request path, the `RequestErrorInterceptor`
    // is added as the last interceptor as it has to be executed last. 
    interceptors: [requestInterceptor, requestErrorInterceptor] 
});

service / on interceptorListener {

    resource function get greeting(@http:Header string checkHeader) 
            returns http:Ok {
        return {
            headers: {
                "checkedHeader": checkHeader
            },
            mediaType: "application/org+json",
            body: {
                message: "Greetings!"
            }
        };
    }
}

Interceptor error handling

Errors that occurred in the request-response pipeline can be intercepted and handled by ResponseErrorInterceptors. In addition, a RequestErrorInterceptor can be used to handle the errors that occurred in the request interceptor execution path. The RequestErrorInterceptor can send a response message according to the error just like a ResponseErrorInterceptor. Moreover, it can modify the request and dipatch it to the target service. For more information, see the HTTP module.

import ballerina/http;
import ballerina/io;
final string check_header = "checkHeader";

Header name checked by the request interceptor.

final string request_check_header_value = "RequestErrorInterceptor";

Header value to be set to the request in the request error interceptor.

service class RequestInterceptor {
    *http:RequestInterceptor;
    resource function 'default [string... path](http:RequestContext ctx, 
            @http:Header string checkHeader) returns http:NextService|error? {
        io:println("Check Header Value : ", checkHeader);
        return ctx.next();
    }
}

This will return a HeaderNotFoundError if you do not set this header. Then, the execution will jump to the nearest RequestErrorInterceptor.

RequestInterceptor requestInterceptor = new;
service class RequestErrorInterceptor {
    *http:RequestErrorInterceptor;

A RequestErrorInterceptor service class implementation. It allows you to intercept the error that occurred in the request path and handle it accordingly. A RequestErrorInterceptor service class can have only one resource function.

    resource function 'default [string... path](error err, http:Request req, 
            http:RequestContext ctx) returns http:NextService|error? {

The resource function inside a RequestErrorInterceptor is only allowed to have the default method and path. The error occurred in the interceptor execution can be accessed by the mandatory argument : error.

        req.setHeader(check_header, request_check_header_value);
        return ctx.next();
    }
}

In this case, a header is set to the request, and then, the modified request is dispatched to the target service. Moreover, you can send different responses according to the error type.

RequestErrorInterceptor requestErrorInterceptor = new;

Creates a new RequestErrorInterceptor.

listener http:Listener interceptorListener = new http:Listener(9090, config = {
    interceptors: [requestInterceptor, requestErrorInterceptor] 
});

To handle all of the errors in the request path, the RequestErrorInterceptor is added as the last interceptor as it has to be executed last.

service / on interceptorListener {
    resource function get greeting(@http:Header string checkHeader) 
            returns http:Ok {
        return {
            headers: {
                "checkedHeader": checkHeader
            },
            mediaType: "application/org+json",
            body: {
                message: "Greetings!"
            }
        };
    }
}
# Run the service.
bal run http_interceptor_error_handling.bal
# Invoke the service.
curl -v http://localhost:9090/greeting
*   Trying ::1:9090...
* Connected to localhost (::1) port 9090 (#0)
> GET /greeting HTTP/1.1
> Host: localhost:9090
> User-Agent: curl/7.77.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse.
< HTTP/1.1 200 OK
< checkedHeader: RequestErrorInterceptor
< content-type: application/org+json
< content-length: 24
< server: ballerina
< date: Tue, 19 Apr 2022 12:37:21 +0530
< 
* Connection #0 to host localhost left intact
{"message":"Greetings!"}