Chapter 2. HTTP Verbs

HTTP verbs such as GET and POST let us send our intention along with the URL so we can instruct the server what to do with it. Web requests are more than just a series of addresses, and verbs contribute to the rich fabric of the journey. This chapter covers how to make and respond to HTTP requests using a selection of common HTTP verbs, including lots of examples.

I mentioned GET and POST because it’s very likely you’re already familiar with those. There are many verbs that can be used with HTTP—in fact, we can even invent our own—but we’ll get to that later in the chapter (see “Using Other HTTP Verbs”). First, let’s revisit GET and POST in some detail, looking at when to use each one and what the differences are between them.

Serving GET Requests

URLs used with GET can be bookmarked, they can be called as many times as needed, and the request should change the data it accesses. A great example of using a GET request when filling in a web form is when using a search form, which should always use GET. Searches can be repeated safely, and the URLs can be shared.

Consider the simple web form in Figure 2-1, which allows users to state which category of results they’d like and how many results to show. The code for displaying the form and the (placeholder) search results on the page could be something like this:

<html>
<head>
<title>GET Form</title>
<link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.6.0/pure-min.css">
</head>
<body>
<div style="margin: 20px">
<h1>A GET Form</h1>

<?php if(empty($_GET)): ?>

<form name="search" method="get" class="pure-form pure-form-stacked">
    Category:
    <select name="category">
        <option value="entertainment">Entertainment</option>
        <option value="sport">Sport</option>
        <option value="technology">Technology</option>
    </select>

    Rows per page: <select name="rows">
        <option value="10">10</option>
        <option value="20">20</option>
        <option value="50">50</option>
    </select>

    <input type="submit" value="Search" class="pure-button pure-button-primary"/>
</form>

<?php else: ?>

<p>Wonderfully filtered search results</p>

<?php endif; ?>

</div>
</body>
</html>

You can see that PHP simply checks if it has been given some search criteria (or indeed any data in the $_GET superglobal) and if not, it displays the empty form. If there was data, then it would process it (although probably in a more interesting way than this trivial example does). The data gets submitted on the URL when the form is filled in (GET requests typically have no body data), resulting in a URL like this:

http://localhost/book/get-form-page.php?category=technology&rows=20

Having the data visible on the URL is a design choice. When this happens, a user can easily bookmark or share this URL with others, which is sometimes very useful, for example, to bookmark a particular set of search results, or a product page. In other use cases, such as submitting a form to update a user’s profile, we really don’t want users to be able to share or save the request that they made, so a POST request would be more appropriate. As software developers, we need to choose whether to submit forms via GET or POST, and in general a good rule of thumb is that if the request is safe to repeat, then GET is a good choice; otherwise use POST. We’ll see more examples of the correct use of verbs in APIs as well as forms during this chapter.

pwsv 0201
Figure 2-1. An example search form

Making GET Requests

The previous example showed how PHP responds to a GET request, but how does it make one? Well, as discussed in Chapter 1, there are many ways to approach this. For a very quick solution, use PHP’s stream handling to create the complete request to send:

<?php

$url = 'http://localhost/book/get-form-page.php';
$data = ["category" => "technology", "rows" => 20];

$get_addr = $url . '?' . http_build_query($data);
$page = file_get_contents($get_addr);
echo $page;

In a Real World™ system, it is prudent to be cautious of the data coming in from external APIs; it is best to filter the contents of $page before outputting it or using it anywhere else. As an alternative to using PHP’s stream features, you could use whatever functionality your existing frameworks or libraries offer, or make use of the cURL extension that is built in to PHP.

Using cURL, our code would instead look like this:

<?php

$url = 'http://localhost/book/get-form-page.php';
$data = ["category" => "technology", "rows" => 20];

$get_addr = $url . '?' . http_build_query($data);
$ch = curl_init($get_addr);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$page = curl_exec($ch);
echo $page;

Either of these approaches works well when you want to fetch data into your PHP script from an external API or page. The examples here show web pages, but they apply when working with HTML, XML, JSON, or anything else.

Handling POST Requests

In contrast to GET requests, a POST request is one that does cause change on the server that handles the request. These requests shouldn’t be repeated or bookmarked, which is why your browser warns you when it is resubmitting data. Let’s use a POST form when the request changes data on the server side. Figure 2-2, for example, involves updating a bit of user profile information.

pwsv 0202
Figure 2-2. Simple form that updates data, sending content via a POST request

When a form is submitted via GET, we can see the variables being sent on the URL. With POST, however, the data goes into the body of the request, and the Content-Type header denotes what kind of data can be found in the body. When we fill in the form in Figure 2-2, the request looks like this:

POST /book/post-form-page.php HTTP/1.1
Host: localhost
Content-Length: 48
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

email=lorna%40example.com&display_name=LornaJane

In this example, you can see the data in the body, with the Content-Type and Content-Length headers set appropriately so that the server can decode the response (more about content negotiation in Chapter 3). Next we’ll look at the server side of the conversation.

PHP knows how to handle form data, so it can parse this out and place the fields into $_POST, so it will be ready for use in the script. Here is the code behind this page, showing the form without any incoming data; if data existed, it would be displayed:

<html>
<head>
<title>POST Form</title>
<link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.6.0/pure-min.css">
</head>
<body>
<div style="margin: 20px">
<h1>A POST Form</h1>

<?php if(empty($_POST)): ?>

<form name="user" method="post" class="pure-form pure-form-stacked">
    Email:
    <input type="text" length="60" name="email" />

    Display name:
    <input type="text" length="60" name="display_name" />

    <input type="submit" value="Go" class="pure-button pure-button-primary"/>
</form>

<?php else:
    echo "New user email: " . filter_input(INPUT_POST,
        "email", FILTER_VALIDATE_EMAIL);
endif; ?>

</div>
</body>
</html>

It is very common to build PHP forms and parse data in this way, but when handling HTTP requests, it is also important to consider how the requests can be made and responded to (spoiler: it looks a lot like our GET request code).

Making POST Requests

To POST data to this form using streams (as in “Making GET Requests”), the same basic approach can be used, but some context should be added to the stream, so it will know which methods, headers, and verbs to use:

<?php

$url = 'http://localhost/book/post-form-page.php';
$data = ["email" => "[email protected]", "display_name" => "LornaJane"];
$options = ["http" =>
    ["method"  => "POST",
        "header"  => "Content-Type: application/x-www-form-urlencoded",
        "content" => http_build_query($data)
    ]
];

$page = file_get_contents($url, NULL, stream_context_create($options));
echo $page;

When POST data is sent to the page created, the data sent appears in the output rather than in the form, so it shows “New user email: [email protected].” This code looks very similar to the previous streams example, but this example uses stream_context_create() to add some additional information to the stream.

You can see that we added the body content as a simple string, formatted it as a URL using http_build_query(), and indicated which content type the body is. This means that other data formats can very easily be sent by formatting the strings correctly and setting the headers.

Here is an example that makes the same POST request again, but this time using Guzzle (these examples are for version 6 of Guzzle):

<?php
require "vendor/autoload.php";

$url = 'http://localhost/book/post-form-page.php';
$data = ["email" => "[email protected]", "display_name" => "LornaJane"];

$client = new GuzzleHttpClient();
$page = $client->post($url, ["form_params" => $data]);
echo $page->getBody();

This looks very similar to the previous example, but using the built-in form_params option to Guzzle means that the Content-Type will be specified for us (there is also a multipart option if you need to send file uploads using Guzzle). When we make the request, we get a response object back rather than a string, but we can access the content using the getBody() method.

Note

In these simple examples, we can make our code make POST requests to HTML forms because the forms have no security features. In reality, most forms will have some CSRF (Cross-Site Request Forgery) protection in them, so you’ll find that you usually can’t make requests like this against forms published on the wider Internet. I would always recommend that you include security features in your own forms—except when you’re trying out the previous examples, of course.

Using Other HTTP Verbs

There are many specifications relating to HTTP, as well as protocols based upon it, and between them they define a wide selection of verbs that can be used with HTTP. Even better, there is always room to invent new HTTP verbs; so long as your client and server both know how to handle a new verb, it is valid to use it. However, be aware that not all elements of network infrastructure between these two points will necessarily know how to handle every verb. Some pieces of network infrastructure do not support PATCH, for example, or the verbs used by the WebDAV protocol. When working with APIs, particularly RESTful ones, it is normal to make use of two additional verbs: PUT and DELETE. REST is covered in detail in Chapter 8, but for now it is useful to examine some examples of how to use these less common verbs in applications.

The simplest of these two is DELETE, because it doesn’t have any body data associated with it. It is possible to see what kind of request was made to a PHP script acting as a server by inspecting the $_SERVER["REQUEST_METHOD"] value, which indicates which verb was used in the request.

To make the request from PHP, it is necessary to set the verb and then make the request as normal. Here’s an example using the cURL extension:

<?php

$url = 'http://localhost/book/example-delete.php';

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_exec($ch);

This example simply issues a request to the $url shown using a DELETE verb.

Using PUT is slightly more involved because, like POST, it can be accompanied by data and the data can be in a variety of formats. In “Handling POST Requests”, I mentioned that for incoming form data, PHP reads form-encoded values for POST and creates a $_POST array for us. There is no equivalent $_PUT superglobal, but we can still make use of the php://input stream to inspect the body data of the request to which the script is sending a response at that time.

When using PHP to respond to PUT requests, the code runs along the lines of this example:

<?php

if($_SERVER['REQUEST_METHOD'] == "PUT") {
    $data = [];
    $incoming = file_get_contents("php://input");
    parse_str($incoming, $data);
    echo "New user email: " . filter_var($data["email"], FILTER_VALIDATE_EMAIL);
} else {
    echo "The request did not use a PUT method";
}

This example inspects the $_SERVER superglobal to see which verb was used, and then responds accordingly. The data coming into this example is form style, meaning it uses file_get_contents() to grab all the body data, then parse_str() to decode it.

Warning

Be careful with parse_str()—if the second argument is omitted, the variables will be extracted as local variables, rather than contained in an array.

In order to use PHP to make a request that the previous script can handle, it is necessary to create the contents of the body of the request and specify that it is a PUT request. Below is an example using the Guzzle library:

<?php

require "vendor/autoload.php";

$url = "http://localhost/book/put-form-page.php";
$data = ["email" => "[email protected]", "display_name" => "LornaJane"];

$client = new GuzzleHttpClient();
$result = $client->put($url, [
    "headers" => ["Content-Type" => "application/x-www-form-urlencoded"],
    "body" => http_build_query($data)
]);

echo $result->getBody();

The PUT verb is specified in this example, and the correct header for the form-encoded data is set. We dictate the data to PUT (manually building the form elements into a string) and then send the request. We will discuss more about other data formats in Chapter 5 and Chapter 6, which cover JSON and XML specifically, but the basic principles of preparing the data and setting the Content-Type header accordingly still stand.

Armed with this knowledge of how to handle GET, POST, DELETE, and PUT verbs, we are able to work with many different kinds of API acting as both a client and as a server. When using other verbs, either those that already exist as part of the HTTP spec or those that are custom to your application, you can use the approaches described here for PUT and DELETE.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset