الخدمية على Laravel و JSON-RPC 2.0

تم بناء بنية SOA (هندسة المنحى الخدمي) من خلال الجمع بين الخدمات المقترنة بشكل فضفاض والتفاعل معها.

للتوضيح ، سننشئ تطبيقين ، العميل والخادم ، وننظم تفاعلهما باستخدام بروتوكول استدعاء الإجراء البعيد 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