Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Unimplemented REST methods should set a 405 status #4808

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 70 additions & 29 deletions library/Zend/Mvc/Controller/AbstractRestfulController.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,29 @@ public function getIdentifierName()
* @param mixed $data
* @return mixed
*/
abstract public function create($data);
public function create($data)
{
$this->response->setStatusCode(405);

return array(
'content' => 'Method Not Allowed'
);
}

/**
* Delete an existing resource
*
* @param mixed $id
* @return mixed
*/
abstract public function delete($id);
public function delete($id)
{
$this->response->setStatusCode(405);

return array(
'content' => 'Method Not Allowed'
);
}

/**
* Delete the entire resource collection
Expand All @@ -102,13 +116,14 @@ abstract public function delete($id);
* (introduced in 2.1.0); instead, raises an exception if not implemented.
*
* @return mixed
* @throws Exception\RuntimeException
*/
public function deleteList()
{
throw new Exception\RuntimeException(sprintf(
'%s is unimplemented', __METHOD__
));
$this->response->setStatusCode(405);

return array(
'content' => 'Method Not Allowed'
);
}

/**
Expand All @@ -117,14 +132,28 @@ public function deleteList()
* @param mixed $id
* @return mixed
*/
abstract public function get($id);
public function get($id)
{
$this->response->setStatusCode(405);

return array(
'content' => 'Method Not Allowed'
);
}

/**
* Return list of resources
*
* @return mixed
*/
abstract public function getList();
public function getList()
{
$this->response->setStatusCode(405);

return array(
'content' => 'Method Not Allowed'
);
}

/**
* Retrieve HEAD metadata for the resource
Expand All @@ -134,13 +163,14 @@ abstract public function getList();
*
* @param null|mixed $id
* @return mixed
* @throws Exception\RuntimeException
*/
public function head($id = null)
{
throw new Exception\RuntimeException(sprintf(
'%s is unimplemented', __METHOD__
));
$this->response->setStatusCode(405);

return array(
'content' => 'Method Not Allowed'
);
}

/**
Expand All @@ -153,13 +183,14 @@ public function head($id = null)
* (introduced in 2.1.0); instead, raises an exception if not implemented.
*
* @return mixed
* @throws Exception\RuntimeException
*/
public function options()
{
throw new Exception\RuntimeException(sprintf(
'%s is unimplemented', __METHOD__
));
$this->response->setStatusCode(405);

return array(
'content' => 'Method Not Allowed'
);
}

/**
Expand All @@ -170,13 +201,14 @@ public function options()
*
* @param $id
* @param $data
* @throws Exception\RuntimeException
*/
public function patch($id, $data)
{
throw new Exception\RuntimeException(sprintf(
'%s is unimplemented', __METHOD__
));
$this->response->setStatusCode(405);

return array(
'content' => 'Method Not Allowed'
);
}

/**
Expand All @@ -187,13 +219,14 @@ public function patch($id, $data)
*
* @param mixed $data
* @return mixed
* @throws Exception\RuntimeException
*/
public function replaceList($data)
{
throw new Exception\RuntimeException(sprintf(
'%s is unimplemented', __METHOD__
));
$this->response->setStatusCode(405);

return array(
'content' => 'Method Not Allowed'
);
}

/**
Expand All @@ -204,13 +237,14 @@ public function replaceList($data)
*
* @param mixed $data
* @return mixed
* @throws Exception\RuntimeException
*/
public function patchList($data)
{
throw new Exception\RuntimeException(sprintf(
'%s is unimplemented', __METHOD__
));
$this->response->setStatusCode(405);

return array(
'content' => 'Method Not Allowed'
);
}

/**
Expand All @@ -220,7 +254,14 @@ public function patchList($data)
* @param mixed $data
* @return mixed
*/
abstract public function update($id, $data);
public function update($id, $data)
{
$this->response->setStatusCode(405);

return array(
'content' => 'Method Not Allowed'
);
}

/**
* Basic functionality for when a page is not available
Expand Down
75 changes: 55 additions & 20 deletions tests/ZendTest/Mvc/Controller/RestfulControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,23 @@
class RestfulControllerTest extends TestCase
{
public $controller;
public $emptyController;
public $request;
public $response;
public $routeMatch;
public $event;

public function setUp()
{
$this->controller = new TestAsset\RestfulTestController();
$this->request = new TestAsset\Request();
$this->response = new Response();
$this->routeMatch = new RouteMatch(array('controller' => 'controller-restful'));
$this->event = new MvcEvent;
$this->controller = new TestAsset\RestfulTestController();
$this->emptyController = new TestAsset\RestfulMethodNotAllowedTestController();
$this->request = new TestAsset\Request();
$this->response = new Response();
$this->routeMatch = new RouteMatch(array('controller' => 'controller-restful'));
$this->event = new MvcEvent;
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
$this->emptyController->setEvent($this->event);
}

public function testDispatchInvokesListWhenNoActionPresentAndNoIdentifierOnGet()
Expand Down Expand Up @@ -388,21 +391,6 @@ public function testRequestingContentTypeReturnsFalseForInvalidMatches($contentT
$this->assertFalse($this->controller->requestHasContentType($this->request, TestAsset\RestfulTestController::CONTENT_TYPE_JSON));
}

public function testDispatchViaPatchWithoutIdentifierReturns405ResponseIfPatchListThrowsException()
{
$entity = new stdClass;
$entity->name = 'foo';
$entity->type = 'standard';
$this->controller->entity = $entity;
$entity = array('name' => __FUNCTION__);
$string = http_build_query($entity);
$this->request->setMethod('PATCH')
->setContent($string);
$result = $this->controller->dispatch($this->request, $this->response);
$this->assertInstanceOf('Zend\Http\Response', $result);
$this->assertEquals(405, $result->getStatusCode());
}

public function testDispatchWithUnrecognizedMethodReturns405Response()
{
$this->request->setMethod('PROPFIND');
Expand Down Expand Up @@ -450,4 +438,51 @@ public function testUsesConfiguredIdentifierNameToGetIdentifier()
$result = $getIdentifier->invoke($this->controller, $this->routeMatch, $this->request);
$this->assertEquals('bar', $result);
}

/**
* @dataProvider testNotImplementedMethodSets504HttpCodeProvider
*/
public function testNotImplementedMethodSets504HttpCode($method, $content, array $routeParams)
{
$this->request->setMethod($method);

if ($content) {
$this->request->setContent($content);
}

foreach ($routeParams as $name => $value) {
$this->routeMatch->setParam($name, $value);
}

$result = $this->emptyController->dispatch($this->request, $this->response);
$response = $this->emptyController->getResponse();

$this->assertEquals(405, $response->getStatusCode());
$this->assertEquals('Method Not Allowed', $this->response->getReasonPhrase());
}

public function testNotImplementedMethodSets504HttpCodeProvider()
{
return array(
array('DELETE', array(), array('id' => 1)), // AbstractRestfulController::delete()
array('DELETE', array(), array()), // AbstractRestfulController::deleteList()
array('GET', array(), array('id' => 1)), // AbstractRestfulController::get()
array('GET', array(), array()), // AbstractRestfulController::getList()
array('HEAD', array(), array('id' => 1)), // AbstractRestfulController::head()
array('HEAD', array(), array()), // AbstractRestfulController::head()
array('OPTIONS', array(), array()), // AbstractRestfulController::options()
array('PATCH', http_build_query(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::patch()
array('PATCH', json_encode(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::patch()
array('PATCH', http_build_query(array('foo' => 1)), array()), // AbstractRestfulController::patchList()
array('PATCH', json_encode(array('foo' => 1)), array()), // AbstractRestfulController::patchList()
array('POST', http_build_query(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::update()
array('POST', json_encode(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::update()
array('POST', http_build_query(array('foo' => 1)), array()), // AbstractRestfulController::create()
array('POST', json_encode(array('foo' => 1)), array()), // AbstractRestfulController::create()
array('PUT', http_build_query(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::update()
array('PUT', json_encode(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::update()
array('PUT', http_build_query(array('foo' => 1)), array()), // AbstractRestfulController::replaceList()
array('PUT', json_encode(array('foo' => 1)), array()), // AbstractRestfulController::replaceList()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @package Zend_Mvc
*/

namespace ZendTest\Mvc\Controller\TestAsset;

use Zend\Mvc\Controller\AbstractRestfulController;

class RestfulMethodNotAllowedTestController extends AbstractRestfulController
{}