Skip to content

Commit 015a399

Browse files
authored
Update Action.php
1 parent e534b1d commit 015a399

File tree

1 file changed

+166
-32
lines changed

1 file changed

+166
-32
lines changed

Action.php

Lines changed: 166 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,195 @@
22

33
namespace georgique\yii2\jsonrpc;
44

5+
use yii\base\InvalidRouteException;
6+
use yii\helpers\Json;
7+
8+
/**
9+
* Class Action
10+
*
11+
* Standalone action responsible for parsing json-rpc request, executing it
12+
* and formatting json-rpc response.
13+
*
14+
* @author George Shestayev [email protected]
15+
* @package georgique\yii2\jsonrpc
16+
*/
517
class Action extends \yii\base\Action {
618

719
/**
8-
* Validates structure of JSON-RPC request.
9-
* @param $params
10-
* @return bool
20+
* Parses json body.
21+
* @param $rawBody
22+
* @return array
1123
* @throws Exception
1224
*/
13-
protected function validateJsonRpcRequest($rpcRequest) {
14-
$compulsoryParams = ['jsonrpc', 'method'];
15-
$optionalParams = ['params', 'id'];
25+
public function parseJsonRpcBody($rawBody) {
26+
try {
27+
$parameters = Json::decode($rawBody, false);
28+
if (!$parameters) {
29+
throw new Exception('Not a valid JSON-RPC 2.0 request.', JSON_RPC_ERROR_PARSE);
30+
}
31+
32+
return $parameters;
33+
}
34+
catch (Exception $e) {
35+
throw new Exception('Not a valid JSON-RPC 2.0 request.', JSON_RPC_ERROR_PARSE);
36+
}
37+
}
38+
39+
40+
/**
41+
* Validates method string and converts it into a route.
42+
* @param $method
43+
* @return bool|string
44+
*/
45+
public function parseMethod($method) {
46+
if (!preg_match('/^[\d\w_.]+/$', $method)) {
47+
return false;
48+
}
49+
50+
$parts = explode($method, '.');
51+
foreach ($parts as $part) {
52+
// There cannot be empty part in route
53+
if (empty($part)) return false;
54+
}
55+
56+
return implode($parts, '/');
57+
}
58+
59+
/**
60+
* Parses request (parsed JSON object) and prepares JsonRpcRequest object.
61+
* @param $request
62+
* @return JsonRpcRequest
63+
* @throws Exception
64+
* @throws \yii\base\InvalidConfigException
65+
*/
66+
public function parseRequest($request) {
67+
if (!isset($request->jsonrpc) || $request->jsonrpc !== '2.0') {
68+
// TODO Customize error message to make the problem clear
69+
throw new Exception("The JSON sent is not a correct JSON-RPC request.", JSON_RPC_ERROR_INVALID_REQUEST);
70+
}
1671

17-
// Checking for compulsory params passed in request
18-
$compulsoryParamsMissing = array_diff($compulsoryParams, array_keys($rpcRequest));
19-
if ($compulsoryParamsMissing) {
20-
throw new Exception('Not a valid JSON-RPC 2.0 request.', 32600);
72+
if (!isset($request->method) || !is_string($request->method) || (!$route = $this->parseMethod($request->method))) {
73+
throw new Exception("The JSON sent is not a correct JSON-RPC request.", JSON_RPC_ERROR_INVALID_REQUEST);
2174
}
2275

23-
// Checking JSON-RPC version
24-
if ($rpcRequest['jsonrpc'] !== '2.0') {
25-
throw new Exception('Not a valid JSON-RPC 2.0 request.', 32600);
76+
$params = null;
77+
if (isset($request->params)) {
78+
if (!is_array($request->params)) {
79+
throw new Exception("The JSON sent is not a correct JSON-RPC request.", JSON_RPC_ERROR_INVALID_REQUEST);
80+
}
81+
$params = $request->params;
2682
}
2783

28-
// Checking if there are non-specification params passed
29-
$wrongParams = array_diff(array_keys($rpcRequest), array_merge($compulsoryParams, $optionalParams));
30-
if ($wrongParams) {
31-
throw new Exception('Not a valid JSON-RPC 2.0 request.', 32600);
84+
if (!isset($request->id)) {
85+
if (!is_scalar($request->id)) {
86+
throw new Exception("The JSON sent is not a correct JSON-RPC request.", JSON_RPC_ERROR_INVALID_REQUEST);
87+
}
3288
}
3389

34-
return true;
90+
return \Yii::createObject([
91+
'class' => JsonRpcRequest::className(),
92+
'id' => $request->id,
93+
'route' => $route,
94+
'params' => $params
95+
]);
3596
}
3697

37-
protected function validationJsonRpcRequestArray($rpcRequestArray) {
38-
if (is_array($rpcRequestArray)) {
39-
$responseArray = [];
40-
foreach ($rpcRequestArray as $rpcRequest) {
41-
$responseArray[] = $this->validateJsonRpcRequest($rpcRequest);
98+
/**
99+
* Parses JSON to an array of JsonRpcRequest.
100+
* @param $params
101+
* @return JsonRpcRequest[]
102+
* @throws Exception
103+
*/
104+
public function parseRequests($params) {
105+
if (is_object($params)) {
106+
$params = [$params];
107+
}
108+
109+
if (is_array($params)) {
110+
$results = [];
111+
foreach ($params as $request) {
112+
try {
113+
$result = $this->parseRequest($request);
114+
}
115+
catch (\Exception $exception) {
116+
if ($exception instanceof Exception) {
117+
if (isset($request->id) && is_string($request->id) || is_int($request->id)) {
118+
$exception->id = $request->id;
119+
}
120+
$result = $exception;
121+
}
122+
else {
123+
$result = new Exception("Error happened during request parsing.", JSON_RPC_ERROR_INTERNAL);
124+
}
125+
}
126+
$results[] = $result;
42127
}
128+
return $results;
43129
}
44-
elseif (is_object($rpcRequestArray)) {
130+
131+
throw new Exception("The JSON sent is not a correct JSON-RPC request.", JSON_RPC_ERROR_INVALID_REQUEST);
132+
}
133+
134+
/**
135+
* Executes JSON-RPC request by route.
136+
* @param JsonRpcRequest $request
137+
* @return Exception|mixed
138+
*/
139+
public function executeRequest($request) {
140+
$route = $request->route;
141+
try {
142+
\Yii::trace("Route requested: '$route'", __METHOD__);
143+
$this->requestedRoute = $route;
144+
$result = \Yii::$app->runAction($request->route, $request->params);
145+
return $result;
146+
}
147+
catch (\Exception $exception) {
148+
if ($exception instanceof InvalidRouteException) {
149+
return new Exception('Method not found.', JSON_RPC_ERROR_METHOD_NOT_FOUND);
150+
}
151+
else if ($exception instanceof Exception) {
152+
return $exception;
153+
}
154+
else {
155+
return new Exception('Internal error.', JSON_RPC_ERROR_INTERNAL);
156+
}
45157
}
46158
}
47159

48160

49161
public function runWithParams($params)
50162
{
51-
$request = \Yii::$app->request;
52-
$bodyParams = $request->getBodyParams();
53-
if (is_object($bodyParams)) {
54-
return 'object';
55-
}
56-
else if (is_array($bodyParams)) {
57-
return 'array';
163+
// Parse errors will be caught and formatted by ErrorHandler
164+
$requests = $this->parseJsonRpcBody(file_get_contents('php://input'));
165+
$requestObjects = $this->parseRequests($requests);
166+
167+
$response = [];
168+
foreach ($requestObjects as $request) {
169+
if ($request instanceof Exception) {
170+
$response[] = [
171+
'jsonrpc' => '2.0',
172+
'error' => $request->toJsonRpcFormat(),
173+
'id' => $request->id
174+
];
175+
}
176+
else {
177+
$executionResult = $this->executeRequest($request);
178+
if ($executionResult instanceof Exception) {
179+
$response[] = [
180+
'jsonrpc' => '2.0',
181+
'error' => $executionResult->toJsonRpcFormat(),
182+
'id' => $request->id
183+
];
184+
} else {
185+
$response[] = [
186+
'jsonrpc' => '2.0',
187+
'result' => $executionResult,
188+
'id' => $request->id
189+
];
190+
}
191+
}
58192
}
59193

60-
return 'nothing';
194+
return (sizeof($response) == 1) ? array_shift($response) : $response;
61195
}
62196
}

0 commit comments

Comments
 (0)