Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This course is archived!
This tutorial uses a deprecated micro-framework called Silex. The fundamentals of REST are still ? valid, but the code we use can't be used in a real application.

Updating the Location Header

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

Updating the Location Header

Hey, we have a working endpoint to view a single programmer! We’re awesome :) Now do you remember the Location response header we return after creating a new programmer? Let’s update that to be a real value.

To do this, first add a bind function to our programmer route:

// src/KnpU/CodeBattle/Controller/Api/ProgrammerController.php
// ...

protected function addRoutes(ControllerCollection $controllers)
{
    $controllers->post('/api/programmers', array($this, 'newAction'));

    $controllers->get('/api/programmers/{nickname}', array($this, 'showAction'))
        ->bind('api_programmers_show');
}

This gives the route an internal name of api_programmers_show. We can use that below to generate a proper URL to the new programmer resource:

// src/KnpU/CodeBattle/Controller/Api/ProgrammerController.php
// ...

public function newAction(Request $request)
{
    // ...

    $response = new Response('It worked. Believe me - I\'m an API', 201);
    $programmerUrl = $this->generateUrl(
        'api_programmers_show',
        ['nickname' => $programmer->nickname]
    );
    $response->headers->set('Location', $programmerUrl);

    return $response;
}

The generateUrl method is a shortcut I added for our app, and it combines the nickname with the rest of the URL. You may make URLs differently in your app, but the idea is the same: set the Location header to the URI where I can GET this new resource.

Tip

The generateUrl method is just a shortcut for doing this:

$programmerUrl = $this->container['url_generator']->generate(
    'api_programmers_show',
    ['nickname' => $programmer->nickname]
);

Update the testing.php script to print out the response from the original POST so we can check this out:

// testing.php
// ...

// 1) Create a programmer resource
$request = $client->post('/api/programmers', null, json_encode($data));
$response = $request->send();

echo $response;
echo "\n\n";
die;

// 2) GET a programmer resource
// ...

And when we run it again, we’ve got a working Location header:

HTTP/1.1 201 Created
...
Location: /api/programmers/ObjectOrienter330

It worked. Believe me - I'm an API

Using the Location Header

The Location header is more than just a nice thing. Its purpose is to help the client know where to go next without needing to hardcode URLs or URL patterns. To prove this, we can update our testing script to read the Location header and use it for the next request. This lets us remove the hardcoded URL pattern we had before:

// testing.php
// ...

// 1) Create a programmer resource
$request = $client->post('/api/programmers', null, json_encode($data));
$response = $request->send();

$programmerUrl = $response->getHeader('Location');

// 2) GET a programmer resource
$request = $client->get($programmerUrl);
$response = $request->send();

echo $response;
echo "\n\n";

If the URL pattern to view a programmer changes in the future, our client won’t break. That’s really powerful. But it’s also where things start to get complicated. More on that later, dear warrior.

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses a deprecated micro-framework called Silex. The fundamentals of REST are still ? valid, but the code we use can't be used in a real application.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "silex/silex": "~1.0", // v1.3.2
        "symfony/twig-bridge": "~2.1", // v2.7.3
        "symfony/security": "~2.4", // v2.7.3
        "doctrine/dbal": "^2.5.4", // v2.5.4
        "monolog/monolog": "~1.7.0", // 1.7.0
        "symfony/validator": "~2.4", // v2.7.3
        "symfony/expression-language": "~2.4" // v2.7.3
    },
    "require-dev": {
        "behat/mink": "~1.5", // v1.5.0
        "behat/mink-goutte-driver": "~1.0.9", // v1.0.9
        "behat/mink-selenium2-driver": "~1.1.1", // v1.1.1
        "behat/behat": "~2.5", // v2.5.5
        "behat/mink-extension": "~1.2.0", // v1.2.0
        "phpunit/phpunit": "~5.7.0", // 5.7.27
        "guzzle/guzzle": "~3.7" // v3.9.3
    }
}
userVoice