Error handling

In the following section, we will explain how to validate the input data in our microservice and how to manage the possible errors. It is important to filter the request we are receiving--not only to notify the consumer that the request was not valid, but also to avoid security problems or parameters that we are not expecting.

Validation

Lumen has a fantastic validation system, so we do not need to install anything to validate our data. Note that the following validation rules can be placed on the routes.php or on the controller inside every function. We will use it inside the function to be clearer.

To use our database for the validation system, we need to configure it. This is very simple; we just need to create a config/database.php file (and the folder) in our root with the following code:

    <?php
    return [
        'default'     => 'mysql',
        'connections' => [
            'mysql' => [
                'driver'    => 'mysql',
                'host'      => env('DB_HOST'),
                'database'  => env('DB_DATABASE'),
                'username'  => env('DB_USERNAME'),
                'password'  => env('DB_PASSWORD'),
                'collation' => 'utf8_unicode_ci'
            ]
        ]
    ];

Then, you have to add the database line in the bootstrap/app.php file:

    $app->withFacades();
    $app->withEloquent();
    $app->configure('database');

Once you have done this, the Lumen validation system is ready. So, let's write the rules to validate the POST method to create a new secret on the Secrets microservice:

    public function create(Request $request)
    {
        $this->validate(
          $request,
          [
            'name'          => 'required|string|unique:secrets,name',
            'latitude'      => 'required|numeric',
            'longitude'     => 'required|numeric',
            'location_name' => 'required|string'
          ]
        );

In the preceding code, we confirm that the parameters should pass the rules. The field 'name' is required; it is a string, and also it should be unique in the secrets table. The field's 'latitude' and 'longitude' are numeric and required too. Also, the 'location_name' field is required and it is a string.

Tip

In the Lumen documentation (https://lumen.laravel.com/docs), you can check out all the available options to validate your inputs.

You can try it out in your Postman; create a POST request with the following application/json parameters to check a failed insertion (note that you can also send it like form-data key values):

    {
        "name": "amber",
        "latitude":"1.23",
        "longitude":"-1.23",
        "location_name": "test"
    }

The preceding request will try to validate a new secret with the same name as other previous records. From our validation rules, we are not allowing the consumers to create new secrets with the same name, so our microservice will respond with a 422 error with the following body:

    {
        "name": [
            "The name has already been taken."
        ]
    }

Note that the status codes (or error codes) are very important to inform your consumers what happened with their requests; if everything is fine, it should respond with a 200 status code. Lumen returns a 200 status code by default.

In Chapter 11Best Practices and Conventions, we will see a full list of all the available codes that you can use in your application.

Once the validation rules pass, we should save the data on the database. This is very simple in Lumen, just do this:

    $secret = Secret::create($request->all());
    if ($secret->save() === false) {
        // Manage Error
    }

After this, we will have our new record available in the database. Lumen provides other methods to create for other tasks such as fill, update, or delete.

Manage exceptions

It is necessary to know that we have to manage the possible errors that happen in our application. To do this, Lumen provides us with a list of exceptions that can be used.

So, now we will try to get an exception when trying to call another microservice. To do this, we will call the secret microservice from the user microservice.

Please remember that for security reasons if you didn't link a container with another container, they can't see each other. Edit your docker-compose.yml and add the link from the microservice_user_fpm to the microservice_secret_nginx, as follows:

    microservice_user_fpm:
        build: ./microservices/user/php-fpm/
        volumes_from:
            - source_user
        links:
            - autodiscovery
            - microservice_secret_nginx
        expose:
            - 9000
        environment:
            - BACKEND=microservice-user-nginx
            - CONSUL=autodiscovery

Now, you should start your containers again:

docker-compose start

Also, remember that we need to install GuzzleHttp as we did before on the Battle microservice and on the User microservice in order to call the Secret microservice.

We will create a new function on the User microservice to show the secrets kept in a user wallet.

Add this to app/Http/routes.php:

    $app->get(
        'user/{id}/wallet', 
        'UserController@getWallet'
    );

Then, we will create the method to get a secret from the user wallet--for example, look at this:

    public function getWallet($id)
    {
        /* ... Code ommited ... */
        $client = new Client(['verify' => false]);
        try {
            $remoteCall = $client->get(
                'http://microservice_secret_nginx                                       /api/v1/secret/1');
        } catch (ConnectException $e) {
            /* ... Code ommited ... */
            throw $e;
        } catch (ServerException $e) {
            /* ... Code ommited ... */
        } catch (Exception $e) {
            /* ... Code ommited ... */
        }
          /* ... Code ommited ... */
    }

We are calling the Secret microservice, but we will modify the URI in order to get a ConnectException, so please modify it:

    $remoteCall = $client->get(
        'http://this_uri_is_not_going_to_work'
    );

Give it a try on Postman; you will receive a ConnectException error.

Now, set the URI correctly again and put some wrong code on the secret microservice side:

    public function get($id)
    {
      this_function_does_not_exist();
    }

The preceding code will return an error 500 for the secret microservice; but we are calling it from the User microservice, so now we will receive a ServerException error.

There are hundreds of kinds of errors that you can manage in your microservice by catching them. In Lumen, all the exceptions are handled by the Handler class (located at app/Exceptions/Handler.php). This class has two defined methods:

  • report(): This allows us to send our exceptions to external services--for example, a centralized logging system.
  • render(): This transforms our exception into an HTTP response.

We will be updating the render() method to return custom error messages and error codes. Imagine that we want to catch a Guzzle ConnectException and return a more friendly and easy-to-manage error. Take a look at the following code:

    /** Code omitted **/
    use SymfonyComponentHttpFoundationResponse;
    use GuzzleHttpExceptionConnectException;

    /** Code omitted **/

    public function render($request, Exception $e)
    {
        switch ($e) {
            case ($e instanceof ConnectException) :
                return response()->json(
                    [
                        'error' => 'connection_error',
                        'code'  => '123'
                    ],
                    Response::HTTP_SERVICE_UNAVAILABLE
                );
                break;
            default :
                return parent::render($request, $e);
               break;
        }   
    }

Here, we are detecting the Guzzle ConnectException and giving a custom error message and code. Using this strategy helps us to know what is failing and allows us to act according to the error we are dealing with. For example, we can assign the code 123 to all our connection errors; so, when we detect this issue, we can avoid a cascade failure in other services or notify the developer.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.94.249