REST api controller and routing

Hello,

I search in the discussions but did not found anything related, please point me if there is already an open conversation.

It would be really nice if there was some thought and code features applied on creating a REST api with a Yii 2.0 application.

Having a REST api is getting a standard for serious web services, and is something that could come with yii 2.0 from core.

I checked what fuelphp does and the below are mostly copycated from there but I think most of it is obvious.

Let’s say we have a specific controller like CRestController (sorry bout the Yii 1.1 style naming). The actions in this controller are not prefixed with ‘action’ but the method that can be accessed like the below example.




<?php

class PeopleController extends CRestContoller

{

    public function getList()

    {

        $this->render(array(

            '0'=>array('...'),

            '1'=>array('...'),

        ));


    }

    public function postPerson()

    {

        ...

    }

    public function putPerson()

    {

        ...

    }

}



I am aware of the


getList

will be confusing and will have issues, so please focus on the idea rather the exact example above.

We could also have the classic action prefix but it would be a nice convention over configuration imho.

In the above example you would notice a "render" function, this is not the classic render but instead give a result in some specific formats like json,xml without having the developer hard times.

If link is like




//Gives json result

http://yii/index.php?r=people/list.json

//Gives the same result in xml format

http://yii/index.php?r=people/list.xml

Let me know what you thing about it or if you have already some ideas.

Friendly and geeky,

Tydeas

I totally agree. REST is standard these days and while you can do the above with REST routes using CUrlManager the real “problem” is the way Yii is rendering results right now. It is very focused on rendering HTML, so to use JSON or XML one has to set the headers and echo JSON strings etc. which feels hacky. There has been a CHttpResponse class in 1.0.x but it was removed for reasons I don’t quite understand. Such a class could hold the logic that either leads to a JSON output or a rendered view file.

I have already had to implement one web service (http://spinitron.com/charts/about) like this in Yii and it was not so much fun. I ended up using a model with various custom validators to process the inputs.

Clearly a cleverly designed framework specifically for such work could help greatly. So I entirely support your proposal.

The scheme you propose of mapping HTTP request methods to action prefixes seems general enough to me.

Output of data is, I think, not hard for the developer.

Where the framework could help a lot is in input processing. I would like to see some kind of configurable mapping between inputs in requests and models representing processed input data. Inputs can reasonably be in conventional php $_GET/POST etc., in custom headers, or in data structures within the request body, or some combination. This mapping and transfer of data to input models can involve very basic validation.

Then comes validation under direct control of a controller action. Yii already offers support for this. But I found it not so easy (I was no Yii expert at the time and I still consider myself a beginner) to implement what you might think of as a validation tree: validate this, then, depending on that input’s value and prior application state, decide where to proceed next in validation. (If you look at the API options in http://spinitron.com/charts/about you might be able to imagine what I mean.) Some complex arrangement of scenarios that map to branches of the tree might work but I haven’t tried.

So those would be my two wishes for the list:

  1. a highly generalized HTTP request to input model mapper

  2. some nice way (I can’t imagine it yet) to use Yii’s validation framework and core validators in a potentially complex input processing state machine

I don’t share that feeling. I have never had any problem with or engineering or aesthetic concern about transmitting data to a client by using echo or print to put an appropriately encoded string in PHP’s output buffer.

Thanks on participating in this conversation. What you described is exactly what I want to point out.

Off course you can do a REST Api with Yii 1.1 but then comes a little bit of pain.

Let’s try to have a basic infrastructure in the Yii 2.0 Core to just provide the basic and obvious.

I think you got me wrong here. It is not the aesthetic concern as I use that approach quite often for simple AJAX calls etc. So echoing JSON isn’t a bad approach at all. But let’s say you already built an app that would lend itself to offer a restful api (e.g. the logic is the same for a classic request and an api request, only the output differs). You would have these options (at least that’s my experience):

  1. Copy the code you already own and rename the thing ApiXXXXXController and don’t render the classic view but a JSON response

  2. Create a module doing more or less the same

  3. Find a way to recognize if a request is an api call or a classic request (don’t know how you would do that without making the controller code more complex)

So my point is that it would be nice to be able to somehow place logic in between the render methods and the actual response.

Greetings,

Hannes

I’m not sure I understand you correctly (you describe what you want not to have rather than what you want to have) but I think you have in mind a scenario in which you start with an existing webapp, and extend it to become a HTTP REST API by swapping its output processing (CHtml, Widget, view rendering, etc.) with something else from a framework.

Technically that is quite feasible. I just have a hard time imagining such a requirement arising in real life. The differences between when a data system interacts with a human and when it interacts with another computer program are not limited to the output data representation. The queries are also different, as are the processes and the output data sets. In less technical terms: what the remote computer program needs from you will not be the same as what the human needs, even if you had a Babel Fish output translator.

OTOH, it is certainly possible that my opinion belies the limits to my imagination.

Maybe I am overcomplicating things here, so let’s give an example. I like how Rails is built with providing both api responses and app responses out of the box. Example:




GET /people/1 //Renders the view file including the layout

GET /people/1.json //Returns a JSON representation of the model

GET /people/1.xml //Returns a XML representation of the model



In Yii you could of course check for a .json or .xml string in the request and render an appropriate view, but you would put additional logic in there like:




//PSEUDO CODE

public function actionView($id)

{

    $model=$this->loadResource(); //checks if model exists, throws 404 otherwise

    

    switch(Yii::app()->request->acceptType)

    {

        case "json":

            $this->renderPartial('view_json',array('model'=>$model)) //doesn't need the layout

            break;

        case "xml":

            $this->renderPartial('view_xml',array('model'=>$model)) //doesn't need the layout

            break;

        default:

            $this->render('view',array('model'=>$model)) //classic app view

    }

}



The decision which view to render could automatically be handled by the render method without me putting additional code in each action

The same idea goes for all classic CRUD actions (if you build them with restful routes in mind). The logic is largely the same, only the representation differs and this shouldn’t be the concern of the controller but either a CHttpResponse object or CView object in my opinion. I know what you mean: What do you do in cases were you put a lot of additional stuff into the app view (via widgets) that you cannot represent in an api response. First, I assume that most of your logic will sit in your model or your controller, so I don’t see this being a problem. e.g.: If you render a listview/gridview widget you usually pass a dataProvider to the view. The “json view” could use the dataProvider to render a JSON array, the normal view will use CListView etc. In case of related models you could nest them in the JSON response.

The biggest thing of using restful API’s is handling error management (20*,30*, 40*). For example it should return 404 errors if a wrong url is accessed, 401’s if permissions are incorrect and errors in your request format for internal api errors. A good way of handling this would be needed. Also there should be correct headers returned for handling cached responses using last-modified and e-tags.

That is a very good point, Jaggi.

Thanks, Haensel, I now better understand what you have in mind.

Wouldn’t it be swell if life were so simple? so simple that all we had to do was produce a model and serialize it in either decorated HTML for human consumers or JSON|*ML for a computer consumer?

There is absolutely nothing wrong with what you suggest and I fully support the proposal to implement it in the framework. And I would be pleased some day to have a problem to which it would be the solution.

Well, Rails does exactly that out of the box for list,view,update,create and delete actions: Rails documentation I don’t see why the same shouldn’t work with Yii

@Haensel, This could be a really good thing and I am not against of having out of the box feautre. But this could not be enough, as @Jabbi and @fsb said. This thinks are not so simple most of the time, with sql database and bad designed schemas you will have to combine more than one model to get the data you want for a view but what you propose is could be usefull in a HMVC architecture.

With the HMVC and your example, a "view" contains information from a list of model foo & a list of bar. You could split the FooContoller and BarController and use them individual for api and combined for human presenations.

What concerns my is that api does not mean mirroring of database through http.

Web Apis dont be a victim of your success is a really interesting article explaining more properly the above and I think the framework is responsible for educating their users.

@tydeas_dr

That link: Web Apis dont be a victim of your success is really good.

I throughly agree that it would be nice to have some sort of render logic to enable different representations to be returned, independently of the controller logic.

I also think it would be nice to add some request processing logic to CHttpRequest to respond more appropriately to Http headers - part of that would be parsing the client Accept header so that our render logic can know what kind of representation the client would prefer (I think this is much more in line with Http standards than using a url suffix such as .xml to show this. There are a few headers that it would be advantageous to parse in this way: most noticeably the Accept header, but also the Accept-Language header (I think Yii does this at the moment but only to get the preferred language, whereas the header is actually more complicated than that). You would then need some kind of matching logic in any CHttpResponse class, to match what is actually available against requested preferences and determine the best match.

I use my own functions as a helper to parse the Accept header, and then try and match rendering to whatever representations I make available, but I then need to override the render functions in order to choose a view based on the matches.

It would also be nice to integrate Http response codes into the framework a little more (things like adding a 201 code for creations, perhaps adding the appropriate Location header as well, and even using a 401 CHttpException when login is required rather than 403, although I appreciate that strictly a 401 should pertain to Http authentication rather than application specific authentication).

I’m not convinced about the need to change controller prefixes though, since I think a controller shouldn’t be restricted to an api (it should be able to provide web representations too, if needed), and it all usually boils down to the basic CRUD action which are already fairly self-explanatory.

There’s my thoughts on the matter anyway…

I once thought about that using themes that get switched by checking the accept headers. Default would be html output and a "json" theme would then render a view with the same name under the "json" theme folder (the layout would be empty of course). Feels hacky and I never really tried that particular approach but it may work

Regarding the accept header parsing: I think that one could be implemented very easily by extending CHttpRequest and it was part of the core already (it was removed in 1.1.5 or something and I really don’t know why)