SOA auf Laravel und JSON-RPC 2.0

SOA (Service Oriented Architecture) wird durch Kombinieren und Interagieren von lose gekoppelten Diensten erstellt.

Zur Demonstration erstellen wir zwei Anwendungen, Client und Server, und organisieren deren Interaktion mithilfe des Protokolls für Remoteprozeduraufrufe JSON-RPC 2.0.

Kunde


Die Client-Anwendung ist eine Site zum Erstellen und Anzeigen bestimmter Inhalte. Der Client enthält keine eigene Datenbank, empfängt und fügt jedoch Daten aufgrund der Interaktion mit der Serveranwendung hinzu.

Auf dem Client stellt die Interaktion eine Klasse bereitJsonRpcClient

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);
    }
}

Wir brauchen eine Bibliothek GuzzleHttp, installieren Sie sie vor.
Wir bilden eine ganz normale POSTAnfrage mit GuzzleHttp\Client. Die wichtigste Einschränkung hierbei ist das Anforderungsformat.
Gemäß der Spezifikation sollte die JSON-RPC 2.0Anfrage wie folgt aussehen:

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

  • jsonrpc Protokollversion, muss "2.0" anzeigen
  • method Methodenname
  • params Array mit Parametern
  • id Anfrage ID

Antworten

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

Wenn die Anfrage mit einem Fehler abgeschlossen wurde, erhalten wir

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

  • jsonrpc Protokollversion, muss "2.0" anzeigen
  • resultErforderliches Feld für ein erfolgreiches Abfrageergebnis. Sollte nicht existieren, wenn ein Fehler auftritt
  • errorErforderliches Feld, wenn ein Fehler auftritt. Sollte bei erfolgreichem Ergebnis nicht existieren
  • id Anforderungskennung vom Client festgelegt

Der Server bildet die Antwort, daher werden wir darauf zurückkommen.

In der Steuerung ist es notwendig, eine Anfrage mit den notwendigen Parametern zu bilden und die Antwort zu verarbeiten.

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']]);
    }
}

Das feste JSON-RPC-Antwortformat macht es einfach, festzustellen, ob die Anforderung erfolgreich war, und Maßnahmen zu ergreifen, wenn die Antwort einen Fehler enthält.

Server


Beginnen wir mit dem Einrichten des Routings. In der Datei routes/api.phphinzufügen

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

Alle vom Server unter der Adresse empfangenen Anforderungen <server_base_uri>/datawerden von der Klasse verarbeitetJsonRpcServer

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());
        }
    }
}

Die Klasse JsonRpcServerbindet die gewünschte Controller-Methode mit den übergebenen Parametern. Und es gibt die von der Klasse generierte Antwort JsonRpcResponsein dem Format zurück, das JSON-RPC 2.0der oben beschriebenen Spezifikation entspricht .

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,
        ];
    }
}

Es bleibt ein Controller hinzuzufügen.

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;
    }
}

Ich sehe keinen Grund, den Controller im Detail zu beschreiben, ganz Standardmethoden. Die Klasse DataCreateenthält die gesamte Logik zum Erstellen eines Objekts sowie zum Überprüfen der Gültigkeit von Feldern mit dem Auslösen der erforderlichen Ausnahme.

Fazit


Ich habe versucht, die Logik der Anwendungen selbst nicht zu komplizieren, sondern mich auf ihre Interaktion zu konzentrieren.
Die Vor- und Nachteile von JSON-RPC sind in einem Artikel gut beschrieben, auf den ich weiter unten verweisen werde. Dieser Ansatz ist beispielsweise bei der Implementierung eingebetteter Formulare relevant.

Verweise



All Articles