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 POST
Anfrage mit GuzzleHttp\Client
. Die wichtigste Einschränkung hierbei ist das Anforderungsformat.Gemäß der Spezifikation sollte die JSON-RPC 2.0
Anfrage wie folgt aussehen:{
"jsonrpc": "2.0",
"method": "getPageById",
"params": {
"page_uid": "f09f7c040131"
},
"id": "54645"
}
jsonrpc
Protokollversion, muss "2.0" anzeigenmethod
Methodennameparams
Array mit Parameternid
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" anzeigenresult
Erforderliches Feld für ein erfolgreiches Abfrageergebnis. Sollte nicht existieren, wenn ein Fehler auftritterror
Erforderliches Feld, wenn ein Fehler auftritt. Sollte bei erfolgreichem Ergebnis nicht existierenid
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.php
hinzufügenRoute::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>/data
werden 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 JsonRpcServer
bindet die gewünschte Controller-Methode mit den übergebenen Parametern. Und es gibt die von der Klasse generierte Antwort JsonRpcResponse
in dem Format zurück, das JSON-RPC 2.0
der 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 DataCreate
enthä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