31

I am using the Slim framework with PHP to create a RESTful API for my app. However, I assumed that the framework would have some way of creating easier JSON outputs rather than just exit($jsonEncodedVariable);.

Am I missing something in the framework, or do I need to use json_encode()... exit($json)... for every method?

All of the data is taken out of the my MySQL database and would then be put into a JSON array depending on what REST request was called.

For example, if /api/posts/all was requested, I would exit() a JSON array of all the posts which each value for its own key, "value" : key.

My question is, is there an easy way, using the slim framework, for exit()'ing JSON code instead of exiting it as plain text?

max_
  • 24,076
  • 39
  • 122
  • 211

17 Answers17

62

Why not just use Slim's Response Object? (also... why exit?)

$dataAry = // Some data array

$response = $app->response();
$response['Content-Type'] = 'application/json';
$response['X-Powered-By'] = 'Potato Energy';
$response->status(200);
// etc.

$response->body(json_encode($dataAry));
// Or echo json_encode($dataAry)

Let me preface by saying I still consider myself a noob so if I'm making errors please correct me so I can learn. But, I was playing with a similar problem/question and I thought I might chime in with 2 cents and archive a little more discussion on the matter. The more information there is about Slim on Stack the better.

I was basically toying with the same thing and I noticed that you were using exit; At first, I was using exit also because echo was including a bunch of HTML and mucking up what was being returned to my AJAX call. When I used exit, it cleanly cut the HTML out but then the Slim response object wasn't changing the response headers as I defined (see above code.)

What I realized was that this isn't how Slim was designed to work. Use echo, not exit. NOTE - Slim Doc:

Whenever you echo() content from within a route callback, the echo()’d content is captured >in an output buffer and later appended to the Response body before the HTTP response is >returned to the client.

That's convenient, but I was not able to echo. What I was messing up on was a bigger problem. Separation of content from behavior. If you're like me, you're setting up a single page application where this code basically sits on index.php. There is initial html that I needed to load up so I included it on that page. What I needed to do was create a cleaner separation. My routing was properly set up and so when people GET '/' the Slim_Views (see Develop Rel.) returns a rendered template of html and js for me. Brilliant!

Now I have all of Slim's tools at disposal and my code is much much cleaner, separate, manageable, and more compliant with http protocols. I guess this is what frameworks are for. :-)

NOTE: I'm not saying all this is what went down on your end, but I thought the question and your setup seemed very similar. It might help another new guy who wanders down this same path.

UPDATE: As @alttag mentions, this answer is getting out of date (Slim 2)

For Slim3, see an answer below or see this page in the documentation

jmk2142
  • 8,581
  • 3
  • 31
  • 47
  • Hmmm. I found your answer here and wanted to give it a go. exit just seemed like cheating... however I had to exit. I am sending my request cross domain and to get ajax to handle the request I had to exit() after my echo json_data – Jake Mar 07 '12 at 23:44
  • 3
    I actually used the solution as you said, and it works very nice. Some tips : if you need to use $app variable inside a lambda function, should add it to the scope lihe this : function() use($app) {}; or you can get the instance with \Slim\Slim::getInstance() regards ! – alexserver Feb 11 '13 at 03:16
  • Oh god, thanks ! I don't know why "header("content-type: application/json"); did not worked for me, but your solution works perfectly ! Great ! :D – TDK Jan 16 '14 at 13:16
  • 3
    alternatively, `$app->response->headers->set('Content-Type', 'application/json');` – Jack James Jul 11 '14 at 16:25
  • 2
    This is the better answer. – chrisbjr Nov 06 '14 at 14:48
  • 1
    This is a good Slim2 answer. For Slim3, see an answer below or see [this page in the documentation](https://www.slimframework.com/docs/v3/objects/response.html#returning-json) – alttag Nov 08 '18 at 18:33
  • Upvote - I haven't used SLIM in a really really long time. I'll include that link as an edit in the old answer. – jmk2142 Nov 09 '18 at 19:00
34
header("Content-Type: application/json");
echo json_encode($result);
exit;

Hint: Using The Slim PHP Framework for Developing REST APIs

hakre
  • 193,403
  • 52
  • 435
  • 836
  • 6
    IMO this should actually have been selected as the answer because it sets the correct content-type header as part of the response. – hashchange Sep 08 '11 at 11:08
  • 1
    I disagree, orangewrap's answer set the content type via the response object rather than the header function. Either method is valid. And why exit? – Brady Emerson Dec 02 '13 at 08:04
  • Only because you can it must not mean that you need to. As always there are many ways to achieve what's asked about, so I won't say that my answer here is more or less right than the other answer. – hakre Dec 02 '13 at 18:04
  • 1
    I disagree, because the answer show the natural Framework options. I [answered](http://stackoverflow.com/a/35686777/694133) with a example using correct options to Slim – jeff_drumgod Sep 21 '16 at 14:50
  • This answer is written in 2011, things are a little bit different now. It's not a good idea to `echo` results, route callbacks should return a PSR response object instead. Also, even back then, why `exit`? – Nima Aug 02 '17 at 06:28
  • @Nima: You already name the time of the answer, and I think that is the major part to it. However as you ask: I think it's important (in the spirit of the slim framework) to give the shortest example. But even then, the exit most likely is a smell, and a `return` should suffice (if even needed). However, STDOUT is not a that bad concept for HTTP responses. I only know Slim a little, but I can imagine Slim deals perfectly well with it. Not trying to say that a *ResponseObject* will give more options. See also previous comment for more options. – hakre Aug 02 '17 at 20:33
  • 1
    Just thought it would be nice to point other visitors to official Slim documentation here https://www.slimframework.com/docs/objects/response.html#returning-json . Because I see a lot of questions on SO asking how to achieve this, which means this questions and its answers probably still get a lot of attention, and since this is the accepted answer some people might rely on it, which is fine because it works, but as I said now there is a method provided by Slim to make this task easier. – Nima Aug 02 '17 at 22:03
  • Nice link, however it doesn't shows the correct answer in case you want to pass an already JSON encoded string ;) – hakre Aug 02 '17 at 22:58
28

Using Slim 3, I'm using this format:

<?php

$app = new \Slim\App();

$app->get('/{id}', function ($request, $response, $args) {
    $id = $request->getAttribute('id');

    return $response->withJSON(
        ['id' => $id],
        200,
        JSON_UNESCAPED_UNICODE
    );
});

On request "/123", result JSON with:

{
  id: "123"
}

More infos read here.

[UPDATE] Added second and third param to withJSON. Second is The HTTP status code, and third is Json encoding options (best for especial chars and others, for example: print "ã" correctly)

jeff_drumgod
  • 378
  • 4
  • 9
  • Is it necessary to return that object? Or would it suffice to return void (no return) but do the rest as our example shows? – hakre Aug 02 '17 at 20:37
  • I don't know if i understood your comment @hakre, but Slim\Http\Response::withJson() is expected 1 argument. You can send a string, for example "foo" and the browser body prints "foo", with header `content-type:application/json`. But normaly you use this content type with JSON, because this my return is a Array. – jeff_drumgod Aug 02 '17 at 23:58
  • @hakre In Slim3 it's necessary to return the modified `$response`, because the response object is immutable. Your changes won't be made if you don't return the updated response. – alttag Nov 08 '18 at 18:29
11

you can extend slim with an output function which output is depending the REST request was called:

class mySlim extends Slim\Slim {
    function outputArray($data) {
        switch($this->request->headers->get('Accept')) {
            case 'application/json':
            default:
                $this->response->headers->set('Content-Type', 'application/json');
                echo json_encode($data);        
        }       
    } 
}

$app = new mySlim();

and use it like this:

$app->get('/test/', function() use ($app) {
    $data = array(1,2,3,4);
    $app->outputArray($data);
});
d3nnis
  • 111
  • 1
  • 2
8

Since everyone has complicated their answers with functions and classes, I'll throw in this simplified answer. The \Slim\Http\Response can do it for you like this:

$app = new \Slim\Slim();

$app->get('/something', function () use ($app) {
    $response = $app->response();
    $response['Content-Type'] = 'application/json';
    $response->status(200);
    $response->body(json_encode(['data' => []]));
});

$app->run();

As you're most likely only returning JSON data it might be a good idea to make an appropriate middleware, see http://www.sitepoint.com/best-practices-rest-api-scratch-introduction/.

Andreas Bergström
  • 13,891
  • 5
  • 59
  • 53
5

I think Slim also provides a middleware object which does this automatically so users of that framework doesnt have to write json_decode and encode on every request, its called the Slim_Middleware_ContentType object.

$app->response()->('application/json');
$app->add(new Slim_Middleware_ContentType());

it does the decoding for you. the decoding works great.But for encoding the last post is great.

Thanks, Dharani

moribvndvs
  • 42,191
  • 11
  • 135
  • 149
  • 1
    Please notice that this has changed and it is now: `$app->add(New \Slim\Middleware\ContentTypes());` – ehime Jun 26 '13 at 18:45
4

I feel your pain. I wanted to make a reusable function, so I made a helpers file, and included this:

function toJSON($app, $content) {
    $response = $app->response;
    $response['Content-Type'] = 'application/json';
    $response->body( json_encode($content) );
};

And then I used it like this:

$app->get('/v1/users/:id', function($id) use ($app)
{
    //instantiate SMM data model
    $model = new user_model($site);

    //get all docs, or one, depending on if query contains a page ID
    $doc = $model->get(array());

    //if the object contains main -- we don't need the outer data.
    toJSON($app, $doc);
});

Edit: I think it would be really nice if there were already functions like this built into the response object for the popular mime types

Kristian
  • 21,204
  • 19
  • 101
  • 176
3

//JSON output in slim3

$app->get('/users', function($request,$response,$args) {

    require 'db_connect.php';

    $stmt = $pdo->query("SELECT * FROM users");
    $result=$stmt->fetchAll(PDO::FETCH_ASSOC);

    if ($stmt->rowCount() > 0) {
        return $response->withStatus(200)
                ->withHeader('Content-Type', 'application/json')
                ->write(json_encode($result));


    }
    else{
        $result = array(
            "status" => "false",
            "message" => "Result not found"
            );
        return $response->withStatus(200)
                ->withHeader('Content-Type', 'application/json')
                ->write(json_encode($result));
    }
});
emccracken
  • 839
  • 12
  • 21
2

why not $response->write(json_encode($dataAry)); instead of echo json_encode($dataAry); ?

k33g_org
  • 530
  • 5
  • 8
2
function _die($array){
   echo json_encode($array);
   exit;
}


$result = mysql_query("SELECT * FROM table");
while($row = mysql_fetch_assoc($result)){
    $array[] = $row;
}

_die($array);
genesis
  • 50,477
  • 20
  • 96
  • 125
  • while I do recommend something along these lines, I really would have liked to see you put it in the context of a typical slim request. – Kristian Aug 06 '13 at 01:16
1

My fix was adding "exit;" at the end of the json print out, my dev server didn't care, but my live server would not trigger the json end event. I did not need to add headers or use json_encode.

pikknz
  • 19
  • 1
0

Use Slim JSON API https://coderwall.com/p/otcphg/create-a-json-restfull-api-using-slim-framework. You can handle JSON output with it.

kguest
  • 3,804
  • 3
  • 29
  • 31
0

I use https://github.com/entomb/slim-json-api for my API written in Slim 2 to enable JSON-response. Init code looks something like this:

function APIRequests () {
    $app = \Slim\Slim::getInstance();
    $app->view(new \JsonApiView());
    $app->add(new \JsonApiMiddleware());
}

$app->group('/api', 'APIRequests', function () use ($app) {
    $app->get('/areas/:id', function ($id) use ($app) {
       $app->render(200, Area::find($id));
    });
});

I really like the abstraction level using middleware and grouping of routes, making it easy to apply different response types to different areas of the app.

emccracken
  • 839
  • 12
  • 21
0

[BEFORE]: Content-Type text/html; charset=UTF-8

Not working with SOAPUI JSON :(

$this->get('get_all', function ($req, $res, $args) {
    $um = new UserModel();

    return $res
       ->withHeader('Content-Type', 'application/json')
       ->getBody()
       ->write(
        json_encode(
            $um->get_all()
        )
    );
});

[AFTER]: Content-Type application/json;charset=utf-8

Working with SOAPUI JSON ;)

$this->get('get_all', function ($req, $res, $args) {
    $um = new UserModel();

    return $res
        ->withHeader('Content-type', 'application/json;charset=utf-8')
        ->withJson($um->get_all());
user3104260
  • 577
  • 4
  • 5
0

You can use in slim3, Slim’s Response object custom method withJson($data, $status, $encodingOptions)

$app->get('/hello/{name}', function ($request, $response, $args) {
    $data['msg']='Hello '.$request->getAttribute('name');
    $newResponse = $response->withJson($data);
});

For more information read here.

Nanhe Kumar
  • 15,498
  • 5
  • 79
  • 71
0

This is how I do it in slim version 2

$app->response->headers->set("Content-Type", 'application/json');
return $app->response->write(json_encode([
    'status' => true,
    'message' => 'Your message'
]));
Sajjad Ashraf
  • 3,754
  • 1
  • 34
  • 35
-1

header("Content-Type : application/json"); echo json_encode($data);