Open alvarofvr opened 7 years ago
For fix now? Can you help me @samdark?
First of all, try updating to 2.0.11.2 then, if it doesn't work try code from master branch. Then report back.
Grazie, thank you! Tomorrow i try it...
I update to 2.0.11.2 version but doesn't work.
Seems you need to adjust your URL manager config a bit:
[
'class' => 'yii\rest\UrlRule',
'controller' => ['1.0/even' => 'api1/even'],
'only' => ['index', 'view'], // THIS
'prefix' => 'api',
],
Sorry @samdark but that does not work.
How exactly it does not work?
Look at (3.) in "What is the expected result?" paragraph:
Request URL: api.localhost.dev/even
Method: POST
Status: 405
Response: EMPTY!!!!
Now the response code is 404, instead 405 (and the response continues to be empty).
Well, 404 is expected. Empty is not. Are you in debug or in production mode? Have you customized error handler?
Response is not empty, my wrong.
What it is then?
Default html code of 404 error.
Then it's all fine, isn't it? You've limited actions to 'index', 'view'
and getting 404 for an action which is not in the list.
Or... maybe I don't understand what the issue is all about...
If i limited actions to 'only' => ['index', 'view']
(or 'except' => ['delete', 'create', 'update']
) .
I expect that the response of this request:
curl -i -H "Accept:application/json" -XPOST "http://api.localhost.dev/api/v1/even"
is an 405 MethodNotAllowedException with JSON response not empty against a 404 NotFoundException.
Look (3.) and (4.) of paragraph named "What is the expected result?".
The issue is visible in the response of (3.) and (4.), but I try to explain again. If i call a default action implemented in Yii with not allowed method the response code is correct(405) but the json response is empty.
In case of i call a custom action the returned code is correct(405) and json response work great, is not empty.
Why this difference? I think is an issue.
Ah... if you need method no allowed then you should not modify URL rules but instead use VerbFilter
in the controller.
Need to check about JSON response...
//i ovveride this for specify my custom action's allowed methods
public function verbs()
{
$verbs = parent::verbs();
$verbs['foo'] = ['GET', 'HEAD'];
return $verbs;
}
You mean I have to override this completely and specify 'index', 'view'
allowed methods?
Yes.
Ok, I tried with this configuration of verbs filter:
public function verbs()
{
$verbs = [
'foo' => ['GET', 'HEAD'],
'index' => ['GET', 'HEAD'],
'view' => ['GET', 'HEAD']
];
return $verbs;
}
Nothing change.
And now I noticed that if i use 'only' => ['index', 'view']
and try to make a DELETE the code returned is a 404 and not go inside beforeAction($event) of yii\filters\VerbFilter .
But if i use 'except' => ['delete', 'create', 'update']
go inside beforeAction($event) of yii\filters\VerbFilter and $event->action->id is 'options'.
I carried out the controls through the Yii classes. The problem is due to a lack in the method "parseRequest" of the class web\UrlRule.
This method attempts to find a match between the request and the url rules. The problem is that the method does not retain the "route" of the request but overwrite with another. This is because the control of the verb returns "false" in parseRequest. Look at comments.
Class web\UrlRule
:
public function parseRequest($manager, $request)
{
if ($this->mode === self::CREATION_ONLY) {
return false;
}
if (!empty($this->verb) && !in_array($request->getMethod(), $this->verb, true)) {
// there is a problem, when try to parse an a not allowed method this return false,
// this method match 'options' action (not correct) and parse with it and use the options route insted original route.
return false;
}
$suffix = (string)($this->suffix === null ? $manager->suffix : $this->suffix);
$pathInfo = $request->getPathInfo();
$normalized = false;
if ($this->hasNormalizer($manager)) {
$pathInfo = $this->getNormalizer($manager)->normalizePathInfo($pathInfo, $suffix, $normalized);
}
if ($suffix !== '' && $pathInfo !== '') {
$n = strlen($suffix);
if (substr_compare($pathInfo, $suffix, -$n, $n) === 0) {
$pathInfo = substr($pathInfo, 0, -$n);
if ($pathInfo === '') {
// suffix alone is not allowed
return false;
}
} else {
return false;
}
}
if ($this->host !== null) {
$pathInfo = strtolower($request->getHostInfo()) . ($pathInfo === '' ? '' : '/' . $pathInfo);
}
if (!preg_match($this->pattern, $pathInfo, $matches)) {
return false;
}
$matches = $this->substitutePlaceholderNames($matches);
foreach ($this->defaults as $name => $value) {
if (!isset($matches[$name]) || $matches[$name] === '') {
$matches[$name] = $value;
}
}
$params = $this->defaults;
$tr = [];
foreach ($matches as $name => $value) {
if (isset($this->_routeParams[$name])) {
$tr[$this->_routeParams[$name]] = $value;
unset($params[$name]);
} elseif (isset($this->_paramRules[$name])) {
$params[$name] = $value;
}
}
if ($this->_routeRule !== null) {
$route = strtr($this->route, $tr);
} else {
$route = $this->route;
}
Yii::trace("Request parsed with URL rule: {$this->name}", __METHOD__);
if ($normalized) {
// pathInfo was changed by normalizer - we need also normalize route
return $this->getNormalizer($manager)->normalizeRoute([$route, $params]);
} else {
return [$route, $params];
}
}
Consequently it is explained why the $action->id is always 'options': Class yii\filters\VerbFilter:
public function beforeAction($event)
{
$action = $event->action->id;
// $action->id is always 'options' when use default rest action, becouse default implemented action /
//with not allowed method return only 'options', 'options' not has verb
//filter and not throw the exception.
if (isset($this->actions[$action])) {
$verbs = $this->actions[$action];
} elseif (isset($this->actions['*'])) {
$verbs = $this->actions['*'];
} else {
return $event->isValid;
}
$verb = Yii::$app->getRequest()->getMethod();
$allowed = array_map('strtoupper', $verbs);
if (!in_array($verb, $allowed)) {
$event->isValid = false;
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7
Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $allowed));
throw new MethodNotAllowedHttpException('Method Not Allowed. This url can only handle the following request methods: ' . implode(', ', $allowed) . '.');
}
return $event->isValid;
}
It's scheduled to after 2.0.12 release. Will dig into it then.
ok thank you
where did you configured your errorAction? mostlikely the exception is been thrown before the error action is configured. i solved it on a library like this
https://github.com/tecnocen-com/yii2-roa/blob/master/src/modules/ApiContainer.php#L60
maybe we can make a module behavior to do this
@alvarofvr I tried your idea protected function verbs() {
and its working but how to set this as a global. so that i don't have to write in every controller file.
What steps will reproduce the problem?
I built a structure for API Rest in Yii2 (Version 2.0.10). I have this situation:
Class Even with relative modelClass:
Class Odd that extend \yii\rest\ActiveController:
Class FooAction that extend \yii\rest\Action:
Component UrlManager in config:
What is the expected result?
I use POSTMAN, when i call my API Rest named "api.localhost.dev" the result is this, look few example:
What do you get instead?
Why using default actions implemented in yii\rest the response is EMPTY? If i want use APIs in a client frontend app with a call in javascript it would be useful to have the response JSON over only status. Now I debug through yii class and i found in class yii\filters\VerbFilter this method:
If i debug action id in case of a call to default implemented action is always "options", instead in case of a call to my custom action the action id is correct(name of my action), so the MethodNotAllowedHttpException not throwed.
Additional info
I think is becouse the default url settings provide by UrlManager is this:
But, my question is: Are there no methods to avoid this? Thank you for your help. Alberto