Fork me on GitHub

6.1 JSON Response

Obviously, we response result to clients with JSON at default.

6.2 Uniform Response Structure

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.

(1)Returning Code: ret

Reference to HTTP Status Code, we make a convention.

  • 200: API response successfully and return data in data field as expected.
  • 4XX: Illegal request from clients, the reason why fail to request is displayed in msg field.
  • 5XX: API service internal error, and no data can be provided.

ret = 200 means response successfully

When 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 request

If 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 error

This 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.

6.3 Business data: 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:

Annotation Format

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.

6.4 Error Message: 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).

6.5 Header Response

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 .

6.6 Why PhalApi DO NOT catch Exception

As we talk about it before, PhalApi DO NOT catch any exceptions which not extends from PhalApi_Exception for the following considerations.

  • Help backend developers to figure out what's the problem fastly with original PHP trace infomation in development environment.
  • Besides, allow server to monitor and to record PHP errors.

6.7 Response with other format, such as JSONP

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';

6.8 Extend Your Response

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();

6.9 Better Advice

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.

  • data.code = 0, login successfully
  • data.code = 1, user not exist
  • data.code = 2, password wrong
  • data.code = 3, user in the blacklist
  • ... ...

6.10 Domain-Specific Designs and Fiat Standard

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.