Laravel和JSON-RPC 2.0上的SOA

SOA(面向服务的体系结构)是通过组合和交互松散耦合的服务而构建的。

为了演示,我们将创建两个应用程序Client和Server,并使用远程过程调用协议组织它们的交互JSON-RPC 2.0

顾客


客户端应用程序是用于创建和显示某些内容的站点。客户端不包含其自己的数据库,但是由于与服务器应用程序的交互而接收并添加了数据。

在客户端上,交互提供了一个类JsonRpcClient

namespace ClientApp\Services;

use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;

class JsonRpcClient
{
    const JSON_RPC_VERSION = '2.0';

    const METHOD_URI = 'data';

    protected $client;

    public function __construct()
    {
        $this->client = new Client([
            'headers' => ['Content-Type' => 'application/json'],
            'base_uri' => config('services.data.base_uri')
        ]);
    }

    public function send(string $method, array $params): array
    {
        $response = $this->client
            ->post(self::METHOD_URI, [
                RequestOptions::JSON => [
                    'jsonrpc' => self::JSON_RPC_VERSION,
                    'id' => time(),
                    'method' => $method,
                    'params' => $params
                ]
            ])->getBody()->getContents();

        return json_decode($response, true);
    }
}

我们需要一个库GuzzleHttp,预安装它。
我们使用形成了一个完全标准的POST请求GuzzleHttp\Client这里的主要警告是请求格式。
根据规范,JSON-RPC 2.0请求应如下所示:

{
    "jsonrpc": "2.0", 
    "method": "getPageById",
    "params": {
        "page_uid": "f09f7c040131"
    }, 
    "id": "54645"
}

  • jsonrpc 协议版本,必须表示“ 2.0”
  • method 方法名称
  • params 带参数的数组
  • id 要求编号

回答

{
    "jsonrpc": "2.0",
    "result": {
        "id": 2,
        "title": "Index Page",
        "content": "Content",
        "description": "Description",
        "page_uid": "f09f7c040131"
    },
    "id": "54645"
}

如果请求完成但有错误,我们得到

{
    "jsonrpc": "2.0",
    "error": {
        "code": -32700,
        "message": "Parse error"
    },
    "id": "null"
}

  • jsonrpc 协议版本,必须表示“ 2.0”
  • result成功查询结果的必填字段。发生错误时不应该存在
  • error发生错误时的必填字段。成功结果不应该存在
  • id 客户设置的请求标识符

服务器构成了答案,因此我们将回到它。

在控制器中,有必要形成具有必要参数的请求并处理响应。

namespace ClientApp\Http\Controllers;

use App\Services\JsonRpcClient;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;

class SiteController extends Controller
{
    protected $client;

    public function __construct(JsonRpcClient $client)
    {
        $this->client = $client;
    }

    public function show(Request $request)
    {
        $data = $this->client->send('getPageById', ['page_uid' => $request->get('page_uid')]);

        if (empty($data['result'])) {
            abort(404);
        }

        return view('page', ['data' => $data['result']]);
    }

    public function create()
    {
        return view('create-form');
    }

    public function store(Request $request)
    {
        $data = $this->client->send('create', $request->all());

        if (isset($data['error'])) {
            return Redirect::back()->withErrors($data['error']);
        }

        return view('page', ['data' => $data['result']]);
    }
}

JSON-RPC固定响应格式可以轻松查看请求是否成功,如果响应包含错误,则可以采取任何措施。

服务器


让我们从设置路由开始。在文件中routes/api.php添加

Route::post('/data', function (Request $request, JsonRpcServer $server, DataController $controller) {
    return $server->handle($request, $controller);
});

服务器在该地址接收到的所有请求<server_base_uri>/data将由该类处理JsonRpcServer

namespace ServerApp\Services;

class JsonRpcServer
{
    public function handle(Request $request, Controller $controller)
    {        
        try {
            $content = json_decode($request->getContent(), true);

            if (empty($content)) {
                throw new JsonRpcException('Parse error', JsonRpcException::PARSE_ERROR);
            }
            $result = $controller->{$content['method']}(...[$content['params']]);

            return JsonRpcResponse::success($result, $content['id']);
        } catch (\Exception $e) {
            return JsonRpcResponse::error($e->getMessage());
        }
    }
}

该类JsonRpcServer将所需的控制器方法与传递的参数绑定在一起。JsonRpcResponse以上述规范的格式返回由类生成的响应JSON-RPC 2.0

use ServerApp\Http\Response;

class JsonRpcResponse
{
    const JSON_RPC_VERSION = '2.0';

    public static function success($result, string $id = null)
    {
        return [
            'jsonrpc' => self::JSON_RPC_VERSION,
            'result'  => $result,
            'id'      => $id,
        ];
    }

    public static function error($error)
    {
        return [
            'jsonrpc' => self::JSON_RPC_VERSION,
            'error'  => $error,
            'id'      => null,
        ];
    }
}

仍然需要添加控制器。

namespace ServerApp\Http\Controllers;

class DataController extends Controller
{
    public function getPageById(array $params)
    {
        $data = Data::where('page_uid', $params['page_uid'])->first();

        return $data;
    }

    public function create(array $params)
    {
        $data = DataCreate::create($params);

        return $data;
    }
}

我认为没有理由详细描述控制器,这是非常标准的方法。该类DataCreate包含创建对象的所有逻辑,并通过引发必要的异常来检查字段的有效性。

结论


我试图不使应用程序本身的逻辑复杂化,而是将重点放在它们的交互上。
JSON-RPC的优缺点在一篇文章中写得很好,我将在下面提供指向该文章的链接。例如,在实现嵌入式表单时,此方法很重要。

参考文献



All Articles