How do I implement REST service description in a Content-type agnostic way?
Background
I am developing a RESTful API for Web Application. Now this app is somewhat different than a standard run-of-mills solution. This app uses a SIP B2BUA (asterisk), PHP+Mysql on Apache & NodeJS.
It is a call center solution that uses asterisk to handle VoIP calls and a LAMP stack for the UI.
Architecture
REST demands that application be stateless. However, due to the very nature of SIP, it cannot be 100% stateless. Thus I am using NodeJS at certain places to keep the real-time communication going.
Now, there is a REST API to all of this. It is designed in a pretty simple way. You have resources like user, call, campaign etc. And a user can initiate HTTP Verbs on these resources.
The Questions
I have arranged the architecture following the MVC pattern. The most salient thing I have done is, enforce Docblocks in the Models. The following is an example of such a class:
abstract class BaseUserInterface implements Interfaces\UserInterface {
protected $usrType = null;
/**
* This is the constructor
*/
public function __construct(Interfaces\User $usrObj){
$this->usrType = ($usrObj instanceof \Model\Agent) ? 'agent':$this->usrType;
$this->usrType = ($usrObj instanceof \Model\Admin) ? 'admin':$this->usrType;
if($this->usrType === null){
throw new \Framework\APIException(__CLASS__." : Insane Type of User Passed while initiating Interface", 500);
}
//Force docblocks
$methodList = $this->listMethods();
foreach($methodList as $method){
$docblock = $this->describe($method);
if(!$docblock){
throw new \Framework\APIException(__CLASS__." : Contract Violated - Method $method Lacks Documentation", 500);
}
}
} //End __construct
/**
* This is the typeOfInterface
*/
public function typeOfInterface(){ //return whether this is admin or agent interface
return $this->usrType;
} //End typeOfInterface
/**
* This is the listMethods
*/
public function listMethods(){ //Allowed Methods
return get_class_methods($this);
} //End listMethods
/**
* This is the describe
*/
public function describe($methodName){
$methodList = $this->listMethods();
if(!in_array($methodName, $methodList)){
throw new \Framework\APIException(__CLASS__." : Asked To Describe Insane Method - ".$methodName, 400);
}
$class = get_class($this);
$reflector = new \ReflectionClass($class);
$phpdoc = $reflector->getMethod($methodName)->getDocComment();
if($phpdoc){
return $this->docParse($phpdoc);
}
else {
return false;
}
} //End describe
/**
* This is the DocParser
*/
protected function docParse($phpdoc){
$phpdoc = new \phpDocumentor\Reflection\DocBlock($phpdoc);
return $phpdoc->getShortDescription()."<br />";
} //End docParse
abstract public function logout();
Of course, I have given an idea of the docblocks and not the complete docblocks here. Basically, they would have the necessary documentation. Now, all the classes in Model namespace that would extend this class would have to use docblocks thereby enforcing documentation.
Also, I have a Helper::convert() function that can convert any result between XML and JSON depending on the user's Accept Request in the HTTP Header.
So, I am all set with that.
What I am utterly confused with is:
1. I want the user to be able to ask for Describe()
on available methods
at a given resource
. How do I implement that via HTTP headers?
OPTIONS
in HTTP tells me to respond withallowed VERBS on ***this*** resource
.I want to have a common gateway to the app. User asks for /index.php. I respond with
user, call, campaign
and a brief message telling them that they can useGET
on theseresources
to know more. Then upon those resources, I want to displayavailable methods
followed bydescription of the selected method
and so on and so forth.Most importantly, I want to respond in the user's chosen accept-header if supported by me.
How do I go about doing this following correct conventions? Also, some references to RFCs would be really appreciated.
How do I go about doing this following correct conventions?
-- You can't. You're creating your own conventions, so by definition, they're not going to be "standard" or "best practice", but actually your "standard" and your "practice." There's nothing necessarily wrong with that, by the way.