Obviously, we response result to clients with JSON at default.
As we can see above, default API Default.Index
return something like this.
{
"ret": 200,
"data": {
"title": "Hello World!",
"content": "Hi PHPer, welcome to use PhalApi!",
"version": "1.3.6",
"time": 1489910945
},
"msg": ""
}
The response is mainly consisted of three parts. ret
is returning code, which can tell what status is, data
is anything you want to
transfer to clients, while msg
is represent for error message when fail to request. We will explain them one by one in detail.
ret
Reference to HTTP Status Code, we make a convention.
data
field as expected. msg
field. ret = 200
means response successfullyWhen the ret
is equals to 200, it means API response successfully. Then clients can retrieve more business data in data
field that API service provide.
ret = 4XX
means illegal requestIf clients request incorrectly, such as requesting a non-exists API serivce, missing required API params or failling to pass verification, they will get a returning code range from 400 to 499. When this happen, client developers should adjust how to request the API service and ensure they can request correctly. And the reason why fail to request can be found in msg
field.
At backend, we can use PhalApi_Exception_BadRequest to specify returning code and error message. Take signature verification as an example. When the signature from client is wrong, we should throw an exception to interupt the request and warn the clients.
throw new PhalApi_Exception_BadRequest('wrong sign', 1);
Finally, clients get a response where ret
is 401 and msg
is Illegal request: wrong sign
. Please note that, PhalApi will add the exception code with 400 automatically, i.e. 401 = 1 + 400
. That is why we get ret = 401
, not ret = 1
. Therefore, the exception code passed to PhalApi_Exception_BadRequest
should between 1 and 99.
ret = 5XX
means internal errorThis case should not happen generally. When client developers get this, they usually can do nothing but warn backend developers what's going on. As backend developers, we should fix it as soon as possible according the error message in msg
field. We need to check whether we break the rule of PhalApi, or do something wrong such as trying to get an non-exsist field.
In this case, PhalApi will occur a PhalApi_Exception_InternalServerError exception.
data
The data
field is the bridge between API service and cleint APP. It can be any type, and it's up to backend. But it's a rule of thumb to return data
as an array for extendibility and compatibility.
When developing an API, we can add annotation to specify clearly what we will return to clients. Then client developers can visit the online document and check them out.
Let's look into Default.Index
again.
/**
* Default API service
* @return string title title
* @return string content content
* @return string version versio with format: X.X.X
* @return int time current timestamp
*/
public function index() {
return array(
'title' => 'Hello World!',
'content' => T('Hi {name}, welcome to use PhalApi!', array('name' => $this->username)),
'version' => PHALAPI_VERSION,
'time' => $_SERVER['REQUEST_TIME'],
);
}
Then open one browser and visit the online API detail document as beblow.
http://demo.phalapi.net/checkApiParams.php?service=Default.Index
The document looks like:
Reference to return
annotation in PHP Documentor 2, the annotation format is similar to PHPDocs.
@return [Type] [The path of field name, split with dot] [Description of the field]
In above, the type
should be one of these.
keyword | Description |
---|---|
string | String, such as "PhalApi". |
int | Integer, such as 2017. |
float | Float number, such as 3.17. |
boolean | Boolean, i.e. true or false. |
date | Date and timestamp, such as "2017-03-19 17:40:50" or 1489916450. |
array | Array, such as [1, 2, 3] |
fixed | Fixed value |
enum | Enumeration type |
object | Object, such as {'name' : 'PhalApi'} |
NOTE: The difference between array
and object
is that there are no string but only naturnal number indexes in array
and each item has the same type, while object
is stand for a structure, something like a map.
Here are some examples of an array
.
["dogstar", "Aevit", "uwin"] // all item are string
[28, 27, 23] // all item are integer
// all item are structure with same fields name and age
[
{
"name": "dogstar",
"age": 28
},
{
"name": "Aevit",
"age": 27
},
{
"name": "uwin",
"age": 23
}
]
Here is an example of a object
.
{
"name": "dogstar",
"age": 28
}
In order to tell client developers clearly what kind of type we will return to them, especially from array
or object
, we recommend add square brackets behind the field name.
* @return array list user info list
* @return int list[].id user ID
* @return string list[].name username
* @return string list[].note user note
Here, we make it clear that we will return list
as an array to clients. Besides, these square brackets indicate that id
, name
, and note
come from an array
, not a object
, which also indicates it's possible there are multiple user in the list.
NOTE: You can add more description for API service with
@desc
annotation.
msg
As all know, msg
is short for message, i.e. error message. When the returning code is not equals to 200, the msg
should not be empty. If there is something wrong, probably illegal client request or server interal error, PhalApi will automatically transfer exception message as msg
.
NOTE: Only exception class which extends from PhalApi_Exception
can reponse with ret
, data
and msg
. Throwing any other exception classes which not extend from PhalApi_Exception will response nothing to clients, no ret
, no data
, neither msg
.
If we throw a PhalApi_Exception_BadRequest exception as below.
throw new PhalApi_Exception_BadRequest(T('wrong sign'), 1);
The msg
will be "Bad Request: wrong sign", and ret
will be 401(401 = 1 + 400
).
If we throw a PhalApi_Exception_InternalServerError exception as below.
throw new PhalApi_Exception_InternalServerError(T('system is busy now'), 2);
Then the msg
will be "Interal Server Error: system is busy now", and ret
will be 502(502 = 2 + 500
).
When response via HTTP/HTTPS, we can use PhalApi_Response::addHeaders() to add more header information as we want. Assume that we allow all other domain visiting our API services, we can add Access-Control-Allow-Origin
to the headers int file init.php
as below.
DI()->response->addHeaders('Access-Control-Allow-Origin', '*');
In addition, PhalApi add Content-Type
for JSON response.
Content-Type:"application/json;charset=utf-8"
Please note that the same header will cover the former ones, as .
As we talk about it before, PhalApi DO NOT catch any exceptions which not extends from PhalApi_Exception
for the following considerations.
When we develop H5 light app, we may need to response with JSONP, allowing JavaScript do more things. If so, we have to register DI()->response
with PhalApi_Response_JsonP
in entrace file.
if (!empty($_GET['callback'])) {
DI()->response = new PhalApi_Response_JsonP($_GET['callback']);
}
When in unit tests, we do not need a real response as in the browser, but a mock response without no affects. Then we can use PhalApi_Response_Explorer, just displaying result on the console.
DI()->response = 'PhalApi_Response_Explorer';
If you want to response with other format, such as XML, you should implement your own response firstly, which should extend from PhalApi_Response.
class MyResponse_XML extends PhalApi_Response {
protected function formatResult($result) {
//TODO: tranfer $result from array into xml ......
}
}
Secondly, register DI()->response
again.
DI()->response = new MyResponse_XML();
In most time, client need to handle different sutiations according to the operation status from server in business scene. But there is only one returning code for client developers, which is not for final users. Consequently, we suggest to add one more status in the field data
, which is used for business logic because field ret
has been used for technology need.
For example, adding a code
into data
, i.e. the full path is data.code
. When code is equals to zero, it means to be successful, otherwise if fails.
Take User.Login
as an example.
There are four kinds of standard in RESTful Web APIs. And they are Fiat Standard, Personal Standard, Company Standard, and Open Standard.
Obviously, the format JSON + ret-data-msg we recommend here, is not belong to Personal Standard, not to Company Standard either. And it's far away from Open Standard. I think it belong to Fiat Standard in some degree.
Anyway, we hope we could have a good common sense at API service development.