-
-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Request::getBody(), see #57 #58
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ | |
* @property-read string|NULL $remoteAddress | ||
* @property-read string|NULL $remoteHost | ||
* @property-read string|NULL $rawBody | ||
* @property-read string|NULL $body | ||
*/ | ||
class Request implements IRequest | ||
{ | ||
|
@@ -58,9 +59,14 @@ class Request implements IRequest | |
/** @var callable|NULL */ | ||
private $rawBodyCallback; | ||
|
||
/** @var callable|NULL */ | ||
private $bodyCallback; | ||
|
||
|
||
public function __construct(UrlScript $url, $query = NULL, $post = NULL, $files = NULL, $cookies = NULL, | ||
$headers = NULL, $method = NULL, $remoteAddress = NULL, $remoteHost = NULL, $rawBodyCallback = NULL) | ||
public function __construct( | ||
UrlScript $url, $query = NULL, $post = NULL, $files = NULL, $cookies = NULL, | ||
$headers = NULL, $method = NULL, $remoteAddress = NULL, $remoteHost = NULL, | ||
$rawBodyCallback = NULL, $bodyCallback = NULL) | ||
{ | ||
$this->url = $url; | ||
if ($query !== NULL) { | ||
|
@@ -75,6 +81,7 @@ public function __construct(UrlScript $url, $query = NULL, $post = NULL, $files | |
$this->remoteAddress = $remoteAddress; | ||
$this->remoteHost = $remoteHost; | ||
$this->rawBodyCallback = $rawBodyCallback; | ||
$this->bodyCallback = $bodyCallback; | ||
} | ||
|
||
|
||
|
@@ -289,7 +296,18 @@ public function getRemoteHost() | |
*/ | ||
public function getRawBody() | ||
{ | ||
return $this->rawBodyCallback ? call_user_func($this->rawBodyCallback) : NULL; | ||
return $this->rawBodyCallback ? call_user_func($this->rawBodyCallback, $this) : NULL; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMHO rawbody should return rawbody, not parsed body There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? This commit does not change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're using the same callback, that also parses the JSON for application/json, aren't you? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, you've added new parameter, sorry :) |
||
} | ||
|
||
|
||
/** | ||
* Returns parsed content of HTTP request body. | ||
* @return mixed | ||
* @throws InvalidRequestBodyException | ||
*/ | ||
public function getBody() | ||
{ | ||
return $this->bodyCallback ? call_user_func($this->bodyCallback, $this) : NULL; | ||
} | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
namespace Nette\Http; | ||
|
||
use Nette; | ||
use Nette\Utils\Json; | ||
use Nette\Utils\Strings; | ||
|
||
|
||
|
@@ -33,6 +34,25 @@ class RequestFactory | |
/** @var array */ | ||
private $proxies = []; | ||
|
||
/** @var callable[] of function (Request $request): mixed */ | ||
private $bodyParsers = []; | ||
|
||
|
||
public function __construct() | ||
{ | ||
$this->addBodyParser('application/x-www-form-urlencoded', function (Request $request) { | ||
return $request->getPost(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are also files… And files can have the same name as post variables. IMHO support for this type is not useful and is complicated to make it good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Files are not send with application/x-www-form-urlencoded mime type but with multipart/form-data. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you right. But still: it is useful? |
||
}); | ||
|
||
$this->addBodyParser('application/json', function (Request $request) { | ||
try { | ||
return Json::decode($request->getRawBody()); | ||
} catch (Nette\Utils\JsonException $e) { | ||
throw new InvalidRequestBodyException('Body is not a valid JSON', 0, $e); | ||
} | ||
}); | ||
} | ||
|
||
|
||
/** | ||
* @param bool | ||
|
@@ -56,6 +76,18 @@ public function setProxy($proxy) | |
} | ||
|
||
|
||
/** | ||
* @param string | ||
* @param callable function(Request $request): mixed|NULL | ||
* @return self | ||
*/ | ||
public function addBodyParser($contentType, $callback) | ||
{ | ||
$this->bodyParsers[$contentType] = $callback; | ||
return $this; | ||
} | ||
|
||
|
||
/** | ||
* Creates current HttpRequest object. | ||
* @return Request | ||
|
@@ -235,7 +267,22 @@ public function createHttpRequest() | |
return file_get_contents('php://input'); | ||
}; | ||
|
||
return new Request($url, NULL, $post, $files, $cookies, $headers, $method, $remoteAddr, $remoteHost, $rawBodyCallback); | ||
// parsed body | ||
$bodyCallback = function (Request $request) use (& $body) { | ||
if ($body === NULL) { | ||
$contentType = $request->getHeader('Content-Type'); | ||
foreach ($this->bodyParsers as $parserContentType => $parser) { | ||
if (stripos($contentType, $parserContentType) === 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. strcasecmp is better There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You mean strncasecmp? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Its PHP shame that there is no startsWith function. |
||
$body = $parser($request); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
return $body; | ||
}; | ||
|
||
return new Request($url, NULL, $post, $files, $cookies, $headers, $method, $remoteAddr, $remoteHost, $rawBodyCallback, $bodyCallback); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
/** | ||
* This file is part of the Nette Framework (https://nette.org) | ||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com) | ||
*/ | ||
|
||
namespace Nette\Http; | ||
|
||
use Nette; | ||
|
||
|
||
/** | ||
* Exception is thrown when a request body can not be parsed. | ||
*/ | ||
class InvalidRequestBodyException extends \RuntimeException | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<?php | ||
|
||
/** | ||
* Test: Nette\Http\RequestFactory body parsing. | ||
*/ | ||
|
||
use Nette\Http\InvalidRequestBodyException; | ||
use Nette\Http\Request; | ||
use Nette\Http\RequestFactory; | ||
use Nette\Utils\JsonException; | ||
use Tester\Assert; | ||
|
||
|
||
require __DIR__ . '/../bootstrap.php'; | ||
|
||
|
||
$factory = new RequestFactory; | ||
|
||
$setRawBody = function ($rawBody) { | ||
$this->rawBodyCallback = function () use ($rawBody) { | ||
return $rawBody; | ||
}; | ||
}; | ||
|
||
|
||
test(function () use ($factory, $setRawBody) { | ||
$_SERVER = [ | ||
'CONTENT_TYPE' => 'application/json', | ||
]; | ||
|
||
$request = $factory->createHttpRequest(); | ||
$setRawBody->bindTo($request, Request::class)->__invoke('[1, 2.0, "3", true, false, null, {}]'); | ||
Assert::same('[1, 2.0, "3", true, false, null, {}]', $request->getRawBody()); | ||
Assert::equal([1, 2.0, '3', TRUE, FALSE, NULL, new stdClass], $request->body); | ||
}); | ||
|
||
|
||
test(function () use ($factory, $setRawBody) { | ||
$_SERVER = [ | ||
'CONTENT_TYPE' => 'application/json', | ||
]; | ||
|
||
$request = $factory->createHttpRequest(); | ||
$setRawBody->bindTo($request, Request::class)->__invoke('['); | ||
Assert::same('[', $request->getRawBody()); | ||
$e = Assert::exception([$request, 'getBody'], InvalidRequestBodyException::class); | ||
Assert::type(JsonException::class, $e->getPrevious()); | ||
}); | ||
|
||
|
||
test(function () use ($factory, $setRawBody) { | ||
$_SERVER = [ | ||
'CONTENT_TYPE' => 'application/x-www-form-urlencoded', | ||
]; | ||
|
||
$_POST = [ | ||
'a' => 'b', | ||
]; | ||
|
||
$request = $factory->createHttpRequest(); | ||
$setRawBody->bindTo($request, Request::class)->__invoke('a=c'); | ||
Assert::same('a=c', $request->getRawBody()); | ||
Assert::equal(['a' => 'b'], $request->body); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You miss the assignment for bodyCallback.