27

I have this simple REST api, done in Slim,

<?php

require '../vendor/autoload.php';

function getDB()
{
    $dsn = 'sqlite:/home/branchito/personal-projects/slim3-REST/database.sqlite3';

    $options = array(
        PDO::ATTR_PERSISTENT => true,
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    );
    try {

        $dbh = new PDO($dsn);

        foreach ($options as $k => $v)
            $dbh->setAttribute($k, $v);

        return $dbh;
    }
    catch (PDOException $e) {
        $error = $e->getMessage();
    }
}

$app = new \Slim\App();

$app->get('/', function($request, $response) {
    $response->write('Bienvenidos a Slim 3 API');
    return $response;
});

$app->get('/getScore/{id:\d+}', function($request, $response, $args) {

    try {
        $db = getDB();
        $stmt = $db->prepare("SELECT * FROM students
            WHERE student_id = :id
            ");

        $stmt->bindParam(':id', $args['id'], PDO::PARAM_INT);
        $stmt->execute();

        $student = $stmt->fetch(PDO::FETCH_OBJ);

        if($student) {
            $response->withHeader('Content-Type', 'application/json');
            $response->write(json_encode($student));

        } else { throw new PDOException('No records found');}

    } catch (PDOException $e) {

        $response->withStatus(404);
        $err =  '{"error": {"text": "'.$e->getMessage().'"}}';
        $response->write($err);
    }
    return $response;
});

$app->run();

however, I can't get browser to send me application/json content type, it always sends text/html? What I am doing wrong?

EDIT:

Ok, after two hours of hitting the head against the wall, I stumbled upon this answer:

https://github.com/slimphp/Slim/issues/1535 (at the bottom of a page) which explains what happens, appears that response object is immutable and as such it must be returned or reassigned if you want to return it after while.

branquito
  • 3,864
  • 5
  • 35
  • 60

4 Answers4

32

So, instead of this:

if($student) {
            $response->withHeader('Content-Type', 'application/json');
            $response->write(json_encode($student));
            return $response;

        } else { throw new PDOException('No records found');}

Do like this:

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

} else { throw new PDOException('No records found');}

And all is well and good.

branquito
  • 3,864
  • 5
  • 35
  • 60
13

For V3, withJson() is available.

So you can do something like:

return $response->withStatus(200)
                ->withJson(array($request->getAttribute("route")
                ->getArgument("someParameter")));

Note: Make sure you return the $response because if you forget, the response will still come out but it will not be application/json.

Sufian
  • 6,405
  • 16
  • 66
  • 120
conrad10781
  • 2,223
  • 19
  • 17
  • Why is chaining working while having them on separate lines won't? – Jomar Sevillejo Nov 06 '16 at 08:54
  • @JomarSevillejo , I would need to see your code to provide a hint, but if they're on separate lines, you'll still need to return the object after those lines. – conrad10781 Nov 28 '16 at 11:42
  • 1
    @jomar-sevillejo This is because the Response object is immutable. Every time you want to "modify" one of its property, all you do in fact is create a clone with the new value for this property. Therefore, chaining works, and this is the last one that you must return to Slim. – Gabriel Mar 15 '17 at 14:34
  • To add: The reason `->withJson()` works is that not only does it set the body, it automatically adds a the header `Content-Type: application/json;charset=utf-8`. As others have noted, the Response object is immutable, so this returns a modified copy, which must then be returned by the route function. – alttag Nov 08 '18 at 18:24
2

For V3, the simplest method as per the Slim docs is:

$data = array('name' => 'Rob', 'age' => 40);
return $response->withJson($data, 201);

This automatically sets the Content-Type to application/json;charset=utf-8 and lets you set a HTTP status code too (defaults to 200 if omitted).

benyafai
  • 144
  • 6
1

You can also use:

$response = $response->withHeader('Content-Type', 'application/json');
$response->write(json_encode($student));
return $response;

because withHeader return new response object. That way you have more then one write and code between.

jcubic
  • 61,973
  • 54
  • 229
  • 402