mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
ENHANCEMENT: Restful service returns cached response on http and curl errors (from r108437)
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@112750 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
6a83da1455
commit
d60ef78ba4
@ -101,17 +101,12 @@ class RestfulService extends ViewableData {
|
|||||||
* @todo Pass the response headers to RestfulService_Response
|
* @todo Pass the response headers to RestfulService_Response
|
||||||
*
|
*
|
||||||
* This is a replacement of {@link connect()}.
|
* This is a replacement of {@link connect()}.
|
||||||
|
*
|
||||||
|
* @return RestfulService_Response - If curl request produces error, the returned response's status code will be 500
|
||||||
*/
|
*/
|
||||||
public function request($subURL = '', $method = "GET", $data = null, $headers = null, $curlOptions = array()) {
|
public function request($subURL = '', $method = "GET", $data = null, $headers = null, $curlOptions = array()) {
|
||||||
$url = $this->baseURL . $subURL; // Url for the request
|
|
||||||
if($this->queryString) {
|
$url = $this->getAbsoluteRequestURL($subURL);
|
||||||
if(strpos($url, '?') !== false) {
|
|
||||||
$url .= '&' . $this->queryString;
|
|
||||||
} else {
|
|
||||||
$url .= '?' . $this->queryString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$url = str_replace(' ', '%20', $url); // Encode spaces
|
|
||||||
$method = strtoupper($method);
|
$method = strtoupper($method);
|
||||||
|
|
||||||
assert(in_array($method, array('GET','POST','PUT','DELETE','HEAD','OPTIONS')));
|
assert(in_array($method, array('GET','POST','PUT','DELETE','HEAD','OPTIONS')));
|
||||||
@ -171,25 +166,53 @@ class RestfulService extends ViewableData {
|
|||||||
$responseBody = curl_exec($ch);
|
$responseBody = curl_exec($ch);
|
||||||
$curlError = curl_error($ch);
|
$curlError = curl_error($ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
if($curlError !== '' || $statusCode == 0) $statusCode = 500;
|
||||||
|
|
||||||
if($responseBody === false) {
|
$response = new RestfulService_Response($responseBody, $statusCode);
|
||||||
user_error("Curl Error:" . $curlError, E_USER_WARNING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$response = new RestfulService_Response($responseBody, curl_getinfo($ch, CURLINFO_HTTP_CODE));
|
|
||||||
|
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
|
|
||||||
// Serialise response object and write to cache
|
if($curlError === '' && !$response->isError()) {
|
||||||
$store = serialize($response);
|
// Serialise response object and write to cache
|
||||||
file_put_contents($cache_path,$store);
|
$store = serialize($response);
|
||||||
|
file_put_contents($cache_path, $store);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// In case of curl or/and http indicate error, populate response's cachedBody property
|
||||||
|
// with cached response body with the cache file exists
|
||||||
|
if (@file_exists($cache_path)) {
|
||||||
|
$store = file_get_contents($cache_path);
|
||||||
|
$cachedResponse = unserialize($store);
|
||||||
|
|
||||||
|
$response->setCachedBody($cachedResponse->getBody());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$response->setCachedBody(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a full request url
|
||||||
|
* @param string
|
||||||
|
*/
|
||||||
|
function getAbsoluteRequestURL($subURL) {
|
||||||
|
$url = $this->baseURL . $subURL; // Url for the request
|
||||||
|
if($this->queryString) {
|
||||||
|
if(strpos($url, '?') !== false) {
|
||||||
|
$url .= '&' . $this->queryString;
|
||||||
|
} else {
|
||||||
|
$url .= '?' . $this->queryString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str_replace(' ', '%20', $url); // Encode spaces
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets attributes as an array, of a particular type of element.
|
* Gets attributes as an array, of a particular type of element.
|
||||||
* Example : <photo id="2636" owner="123" secret="ab128" server="2">
|
* Example : <photo id="2636" owner="123" secret="ab128" server="2">
|
||||||
@ -362,6 +385,12 @@ class RestfulService extends ViewableData {
|
|||||||
class RestfulService_Response extends SS_HTTPResponse {
|
class RestfulService_Response extends SS_HTTPResponse {
|
||||||
protected $simpleXML;
|
protected $simpleXML;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean It should be populated with cached content
|
||||||
|
* when a request referring to this response was unsuccessful
|
||||||
|
*/
|
||||||
|
protected $cachedBody = false;
|
||||||
|
|
||||||
function __construct($body, $statusCode = 200, $headers = null) {
|
function __construct($body, $statusCode = 200, $headers = null) {
|
||||||
$this->setbody($body);
|
$this->setbody($body);
|
||||||
$this->setStatusCode($statusCode);
|
$this->setStatusCode($statusCode);
|
||||||
@ -380,6 +409,20 @@ class RestfulService_Response extends SS_HTTPResponse {
|
|||||||
return $this->simpleXML;
|
return $this->simpleXML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function getCachedBody() {
|
||||||
|
return $this->cachedBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string
|
||||||
|
*/
|
||||||
|
function setCachedBody($content) {
|
||||||
|
$this->cachedBody = $content;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an array of xpath matches
|
* Return an array of xpath matches
|
||||||
*/
|
*/
|
||||||
|
@ -90,6 +90,41 @@ class RestfulServiceTest extends SapphireTest {
|
|||||||
$test1 = $connection->request('RestfulServiceTest_Controller/invalid?usetestmanifest=1&flush=1');
|
$test1 = $connection->request('RestfulServiceTest_Controller/invalid?usetestmanifest=1&flush=1');
|
||||||
$test1->xpath("\\fail");
|
$test1->xpath("\\fail");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testHttpErrorWithoutCache() {
|
||||||
|
$connection = new RestfulService(Director::absoluteBaseURL(), 0);
|
||||||
|
$response = $connection->request('RestfulServiceTest_Controller/httpErrorWithoutCache?usetestmanifest=1&flush=1');
|
||||||
|
|
||||||
|
$this->assertEquals(400, $response->getStatusCode());
|
||||||
|
$this->assertFalse($response->getCachedBody());
|
||||||
|
$this->assertContains("<error>HTTP Error</error>", $response->getBody());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function testHttpErrorWithCache() {
|
||||||
|
$subUrl = 'RestfulServiceTest_Controller/httpErrorWithCache?usetestmanifest=1&flush=1';
|
||||||
|
$connection = new RestfulService(Director::absoluteBaseURL(), 0);
|
||||||
|
$this->createFakeCachedResponse($connection, $subUrl);
|
||||||
|
$response = $connection->request($subUrl);
|
||||||
|
|
||||||
|
$this->assertEquals(400, $response->getStatusCode());
|
||||||
|
$this->assertEquals("Cache response body",$response->getCachedBody());
|
||||||
|
$this->assertContains("<error>HTTP Error</error>", $response->getBody());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate cached response file for testing error requests that are supposed to have cache files
|
||||||
|
*/
|
||||||
|
private function createFakeCachedResponse($connection, $subUrl) {
|
||||||
|
$fullUrl = $connection->getAbsoluteRequestURL($subUrl);
|
||||||
|
$cachedir = TEMP_FOLDER; // Default silverstripe cache
|
||||||
|
$cache_file = md5($fullUrl); // Encoded name of cache file
|
||||||
|
$cache_path = $cachedir."/xmlresponse_$cache_file";
|
||||||
|
$cacheResponse = new RestfulService_Response("Cache response body");
|
||||||
|
$store = serialize($cacheResponse);
|
||||||
|
file_put_contents($cache_path, $store);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RestfulServiceTest_Controller extends Controller {
|
class RestfulServiceTest_Controller extends Controller {
|
||||||
@ -134,6 +169,29 @@ XML;
|
|||||||
header('Content-type: text/xml');
|
header('Content-type: text/xml');
|
||||||
echo $out;
|
echo $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function httpErrorWithoutCache() {
|
||||||
|
$out = <<<XML
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<test>
|
||||||
|
<error>HTTP Error</error>
|
||||||
|
</test>
|
||||||
|
XML;
|
||||||
|
|
||||||
|
$this->response->setBody($out);
|
||||||
|
$this->response->setStatusCode(400);
|
||||||
|
$this->response->addHeader('Content-type', 'text/xml');
|
||||||
|
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The body of this method is the same as self::httpErrorWithoutCache()
|
||||||
|
* but we need it for caching since caching using request url to determine path to cache file
|
||||||
|
*/
|
||||||
|
public function httpErrorWithCache() {
|
||||||
|
return $this->httpErrorWithoutCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user