boomdraw/rpc-core

Lumen JSON-RPC concern

v1.0.1 2020-12-25 03:12 UTC

This package is auto-updated.

Last update: 2024-10-25 12:33:41 UTC


README

Allows Lumen to handle JSON-RPC 2.0 requests and return JSON-RPC 2.0 responses.

Build Status StyleCI Code Coverage Quality Score Latest Version on Packagist Total Downloads PHP Version License

This package provides a request manager that passes the request to the RpcRequests or default RoutesRequests dispatcher.

Version compatibility

Installation

Via Composer

$ composer require boomdraw/rpc-core

Change the Application class in the bootstrap file to Boomdraw\RpcCore\Application or provide your own Application class with Boomdraw\RpcCore\Concerns\RequestManager trait:

// bootstrap/app.php

$app = new Boomdraw\RpcCore\Application(
    dirname(__DIR__)
);

Change exception handler:

// bootstrap/app.php

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    Boomdraw\RpcCore\Handler::class
);

Your Lumen application is ready to handle JSON-RPC requests!

Usage

RPC endpoint

RPC concern handles only POST requests to the specific endpoint.

By default, it uses current application version config('app.version', '1.0') as a route path (POST /v1.0).

You can provide a custom RPC path by setting it in the app config file:

// config/app.php

return [
    ...
    'rpc' => 'my-custom-uri',
    ...
];

Disable JSON-RPC requests

You can disable the RPC dispatcher in the config/app.php config file:

// config/app.php

return [
    ...
    'rpc' => false,
    ...
];

Disable routes requests

If you want to use your application as RPC only, you can disable default routes dispatcher in the config/app.php config file:

// config/app.php

return [
    ...
    'routes' => false,
    ...
];

JSON-RPC Handlers

Behind the scene, JSON-RPC handlers logic is the same as Controllers. It works with global and routes middleware. You can provide middleware for a handler or its specific method the same way as for Controller.

The handler should extend Boomdraw\RpcCore\Handler class to have the same capabilities as the controller.

Handlers namespace and naming

By default, JSON-RPC requests concern will look for handlers with suffix Handler in the App\Rpc\Handlers namespace. You can change handlers default namespace and/or suffix in the config file.

Create the file config/rpc.php with the next content:

<?php

return [
    /*
    | Handlers default namespace and suffix.
    */
    'handler' => [
        'path' => 'App\Rpc\Handlers',
        'suffix' => 'Handler',
    ],
    /*
     | List of custom handlers with its paths.
     | Handler key must be in a studly caps case
     | Example:
     | 'CustomHelper' => App\Handlers\CustomRpcHandler::class
     */
    'handlers' => [
        //
    ],
];

or copy it from the vendor dir:

cp vendor/boomdraw/rpc-core/config/rpc.php config/rpc.php

Register the config file in the bootstrap:

// bootstrap/app.php

$app->configure('rpc');

Change path and suffix values in the config file as you wish.

Custom handlers

You can provide handlers with a custom name and namespace.

Register rpc config file as described in the step before.

Add to an array with key handlers of config/rpc.php file your handler using the name as a key and its class as a value. The suffix will not be applied to this handler.

You are awesome!

Examples

Handler example

// app/Rpc/Handlers/ExampleHandler.php

<?php

namespace App\Rpc\Handlers;

use Boomdraw\RpcCore\Handler;
use Illuminate\Http\Request;

class ExampleHandler extends Handler
{
    public function __invoke(Request $request)
    {
        return ['data' => ['hello' => $request->hello]];
    }

    public function message(Request $request): string
    {
        return $request->message;
    }

    public function throwException(): void 
    {
        abort(500, 'Yeah!');
    }
}

JSON-RPC request and response examples

POST /v1.0

{
  "jsonrpc": "2.0",
  "method": "Example",
  "params": {"args": {"hello": "world"}, "context": {"userId":  11}},
  "id": 19
}

This request will call method __invoke in the ExampleHandler. RPC response will not wrap returned result because it has been wrapped manually:

{
  "jsonrpc": "2.0",
  "result": {
    "data": {"hello": "world"}
  },
  "id": 19
}

POST /v1.0

{
  "jsonrpc": "2.0",
  "method": "Example.message",
  "params": {"message": "Hello world!"},
  "id": 19
}

This request will call method message in the ExampleHandler. RPC response wraps returned result as an array with data as a key. The params object will be passed to the Request instead of args, because args and context objects are not provided.

{
  "jsonrpc": "2.0",
  "result": {
    "data": "Hello world!"
  },
  "id": 19
}

POST /v1.0

{
  "jsonrpc": "2.0",
  "method": "Example.throwException",
  "id": 19
}

This request will call method throwException in the ExampleHandler. RPC error response will be returned:

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32000,
    "message": "Yeah!"
  },
  "id": 19
}

You can read more about JSON-RPC requests and responses in the official specification.

Context

The data that have been passed to the context object in params will be stored in the request object. You can get this data using context helper function.

// function context(string $key, $default = null)

context('userId', 'default')

Result of this context call will be userId from context object or string default if there is no userId field in the context or it is not a JSON-RPC request.

The JSON-RPC request id is always passed to the context.

Result of calling context('requestId') for previous examples will be 19.

Responses

When handler returns raw data, it will be wrapped to an array with data key if required and passed to Boomdraw\RpcCore\Responses\RpcResponse for the response structure building.

If the handler returns Illuminate\Http\Response or Symfony\Component\HttpFoundation\Response it will be transformed to the Boomdraw\RpcCore\Responses\RpcResponse with data wrapping and HTTP Status Code 200.

If the handlers does not return any data, the Boomdraw\RpcCore\Responses\RpcEmptyResponse will be returned without payload and HTTP Status Code 204.

For non JSON-RPC request application will proceed the response by default way.

Error responses

RPC-core package provides an exception handler that transforms exception regarding JSON-RPC specification.

All error responses will be returned with HTTP Status Code 200.

For the Server Error (code >= 500) will be returned error response with inner code -32000.

For the exceptions with code between 400 and 499 will be returned error response with the same code but with negative sign before it. For example: for 405 error code will be returned response with inner code -405.

Validation exception will be transformed to Boomdraw\RpcCore\Exceptions\InvalidParamsRpcException and returned with code -32602.

Testing

RPC helpers for testing

The package provides a class that makes JSON-RPC handlers testing much easier.

Add to your TestCase.php Boomdraw\RpcCore\Tests\RpcTrait trait.

Now you can use next methods:

send

public function send(string $method, array $args = [], array $context = [])

Transforms provided arguments to the JSON-RPC requests and passes it to the dispatcher.

getRpcData

public function getRpcData(string $key = '')

Extracts field with provided key from RPC response data object or return data as an array if the key is empty.

responseAsArray

public function responseAsArray(): array

Returns response as an array using json_decode.

seeJsonRpc

public function seeJsonRpc(array $data = []): self

Asserts that the response contains provided data.

seeJsonRpcError

public function seeJsonRpcError(array $data = []): self

Asserts that the error response contains provided data.

Package testing

You can run the tests with:

composer test

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details and a todo list.

Security

If you discover any security-related issues, please email pkgsecurity@boomdraw.com instead of using the issue tracker.

License

MIT