<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <title>Blog entries tagged rest :: mwop.net</title>
    <description>Blog entries tagged rest :: mwop.net</description>
    <pubDate>Wed, 19 Sep 2018 08:10:00 -0500</pubDate>
    <generator>Laminas_Feed_Writer 2 (https://getlaminas.org)</generator>
    <link>https://mwop.net/blog/tag/rest</link>
    <atom:link rel="self" type="application/rss+xml" href="https://mwop.net/blog/tag/rest/rss.xml"/>
    <item>
      <title>Notes on GraphQL</title>
      <pubDate>Wed, 19 Sep 2018 08:10:00 -0500</pubDate>
      <link>https://mwop.net/blog/2018-07-18-graphql.html</link>
      <guid>https://mwop.net/blog/2018-07-18-graphql.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>The last week has been my first foray into GraphQL, using the <a href="https://developer.github.com/v4/">GitHub GraphQL
API</a> endpoints. I now have Opinions<sup>TM</sup>.</p>
<p>The promise is fantastic: query for everything you need, but nothing more. Get
it all in one go.</p>
<p>But the reality is somewhat... different.</p>


<p>What I found was that you end up with a lot of garbage data structures that you
then, on the client side, need to decipher and massage, unpacking edges, nodes,
and whatnot. I ended up having to do almost a dozen <code>array_column()</code>,
<code>array_map()</code>, and <code>array_reduce()</code> operations on the returned data to get a
structure I can actually use.</p>
<p>The final data I needed looked like this:</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">[
  {
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"zendframework/zend-expressive"</span>,
    <span class="hljs-string">"tags"</span>: [
      {
        <span class="hljs-string">"name"</span>: <span class="hljs-string">"3.0.2"</span>,
        <span class="hljs-string">"date"</span>: <span class="hljs-string">"2018-04-10"</span>
      }
    ]
  }
]
</code></pre>
<p>To fetch it, I needed a query like the following:</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">query showOrganizationInfo(
  $organization:<span class="hljs-built_in">String</span>!
  $cursor:<span class="hljs-built_in">String</span>!
) {
  organization(login:$organization) {
    repositories(first: <span class="hljs-number">100</span>, <span class="hljs-attr">after</span>: $cursor) {
      pageInfo {
        startCursor
        hasNextPage
        endCursor
      }
      nodes {
        nameWithOwner
        <span class="hljs-attr">tags</span>:refs(refPrefix: <span class="hljs-string">"refs/tags/"</span>, <span class="hljs-attr">first</span>: <span class="hljs-number">100</span>, <span class="hljs-attr">orderBy</span>:{<span class="hljs-attr">field</span>:TAG_COMMIT_DATE, <span class="hljs-attr">direction</span>:DESC}) {
          edges {
            <span class="hljs-attr">tag</span>: node {
              name
              target {
                ... on Commit {
                  pushedDate
                }
                ... on Tag {
                  tagger {
                    date
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
</code></pre>
<p>Which gave me data like the following:</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">{
  <span class="hljs-string">"data"</span>: {
    <span class="hljs-string">"organization"</span>: {
      <span class="hljs-string">"repositories: {
        "</span>pageInfo<span class="hljs-string">": {
          "</span>startCursor<span class="hljs-string">": "</span>...<span class="hljs-string">",
          "</span>hasNextPage<span class="hljs-string">": true,
          "</span>endCursor<span class="hljs-string">": "</span>...<span class="hljs-string">"
        },
        "</span>nodes<span class="hljs-string">": [
          {
            "</span>nameWithOwner<span class="hljs-string">": "</span>zendframework/zend-expressive<span class="hljs-string">",
            "</span>tags<span class="hljs-string">": {
              "</span>edges<span class="hljs-string">": [
                "</span>tag<span class="hljs-string">": {
                  "</span>name<span class="hljs-string">": "</span><span class="hljs-number">3.0</span><span class="hljs-number">.2</span><span class="hljs-string">",
                  "</span>target<span class="hljs-string">": {
                    "</span>tagger<span class="hljs-string">": {
                      "</span>date<span class="hljs-string">": "</span><span class="hljs-number">2018</span><span class="hljs-number">-04</span><span class="hljs-number">-10</span><span class="hljs-string">"
                    }
                  }
                }
              ]
            }
          }
        ]
      }
    }
  }
}
</span></code></pre>
<p>How did I discover how to create the query? I'd like to say it was by reading
the docs. I really would. But these gave me almost zero useful examples,
particularly when it came to pagination, ordering results sets, or what those
various &quot;nodes&quot; and &quot;edges&quot; bits were, or why they were necessary. (I eventually
found the information, but it's still rather opaque as an end-user.)</p>
<p>Additionally, see that <code>pageInfo</code> bit? This brings me to my next point: pagination sucks,
particularly if it's not at the top-level. You can only fetch 100 items at a
time from any given node in the GitHub GraphQL API, which means pagination. And
I have yet to find a client that will detect pagination data in results and
auto-follow them. Additionally, the &quot;after&quot; property had to be something
valid... but there were no examples of what a valid value would be. I had to
resort to StackOverflow to find an example, and I still don't understand why it
works.</p>
<blockquote>
<p>I get why clients cannot unfurl pagination, as pagination data could appear
<em>anywhere</em> in the query. However, it hit me hard, as I thought I had a
complete set of data, only to discover around half of it was missing once I
finally got the processing correct.</p>
</blockquote>
<p>If any items further down the tree <em>also</em> require pagination, you're in for some
real headaches, as you then have to fetch paginated sets depth-first.</p>
<p>So, while GraphQL promises fewer round trips and exactly the data you need, my
experience so far is:</p>
<ul>
<li>
<p>I end up having to be very careful about structuring my queries, paying huge
attention to pagination potential, and often sending multiple queries ANYWAYS.
A well-documented REST API is often far easier to understand and work with
immediately.</p>
</li>
<li>
<p>I end up doing MORE work client-side to make the data I receive back USEFUL.
This is because the payload structure is based on the query structure and the
various permutations you need in order to get at the data you need. Again, a
REST API usually has a single, well-documented payload, making consumption far
easier.</p>
</li>
</ul>
<p>I'm sure I'm probably mis-using GraphQL, or missing a number of features to make
this stuff easier, but so far, I'm left wishing I could just have a number of
useful REST endpoints that I can hit consistently in order to aggregate the data
I need.</p>
<blockquote>
<p>Before anybody suggests it, yes, I am <em>very</em> aware that GitHub also offers a
REST API, and the v3 API has endpoints for most of what I needed. However, I
had to rely on tags, not releases, as not all of our tags have associated
releases. However, the data returned for tags does not include the commit
date; for that, you need to fetch the associated commit, and then the date may
be under either the <code>author</code> or the <code>committer</code>. This approach would have
meant literally thousands of calls to get the data I need, which would have
had me hitting rate limits, and potentially taking hours to complete.</p>
<p>My point: perhaps instead of GraphQL, aggregating a bit more data in REST
resources (e.g., including commit data with tags), or providing endpoints that
allow merging specific resource types could have solved the problem easily.
This is where having a developer relations team that finds out what data
<em>consumers</em> are needing comes in handy, instead of simply mandating <em>graphql
all the things</em> to allow infinite flexibility (and the frustrations of such
flexibility, both for the API developer and consumer).</p>
</blockquote>
<h3>Updates</h3>
<ul>
<li>2018-09-19: syntax highlighting fixes.</li>
</ul>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/2018-07-18-graphql.html">Notes on GraphQL</a> was originally
    published <time class="dt-published" datetime="2018-07-18T17:05:00-05:00">18 July 2018</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>Apigility: Using RPC with HAL</title>
      <pubDate>Wed, 26 Mar 2014 15:30:00 -0500</pubDate>
      <link>https://mwop.net/blog/2014-03-26-apigility-rpc-with-hal.html</link>
      <guid>https://mwop.net/blog/2014-03-26-apigility-rpc-with-hal.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>A few days ago, we <a href="http://bit.ly/ag-1-beta1">released our first beta of Apigility</a>.
We've started our documentation effort now, and one question has arisen a few
times that I want to address: How can you use Hypermedia Application Language
(HAL) in RPC services?</p>


<h2>HAL?</h2>
<p><a href="http://tools.ietf.org/html/draft-kelly-json-hal-06">Hypermedia Application Language</a>
is an IETF proposal for how to represent resources and their relations within
APIs.  Technically, it provides two mediatypes, <code>application/hal+json</code> and
<code>application/hal+xml</code>; however, Apigility only provides the JSON variant.</p>
<p>The important things to know about HAL are:</p>
<ul>
<li>
<p>It provides a standard way of describing relational links. All relational
links are under a <code>_links</code> property of the resource. That property is an
object. Each property of that object is a link relation; the value of each
link relation is an object (or array of such objects) describing the link
that must minimally contain an <code>href</code> proerty. The link object itself can
contain some additional metadata, such as a mediatype, a name (useful for
differentiating between multiple link objects assigned to the same relation).</p>
<p>While not required, the specification recommends resources contain a &quot;self&quot;
relational link, indicating the canonical location for the resource. This is
particularly useful when we consider embedding (the next topic).</p>
<p>Sound hard? It's not:</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">{
    <span class="hljs-string">"_links"</span>: {
        <span class="hljs-string">"self"</span>: {
            <span class="hljs-string">"href"</span>: <span class="hljs-string">"/blog/2014-03-26-apigility-rpc-with-hal"</span>
        }
    }
}
</code></pre>
</li>
<li>
<p>Besides link relations, HAL also provides a standard way of describing
<em>embedded resources</em>. An embedded resource is any other resource you can
address via your API, and, as such, would be structured as a HAL resource —
in other words, it would have a <code>_links</code> property with relational links.
Essentially, any property of the resource you're returning that can itself be
addressed via the URI must be <em>embedded</em> in the resource. This is done via
the property <code>_embedded</code>.</p>
<p>Like <code>_links</code>, <code>_embedded</code> is an object. Each key in the object is the local
name by which the resource refers to the embedded resource. The value of such
keys can either be HAL resources or <em>arrays</em> of HAL resources; in fact, this
is how <em>collections</em> are represented in HAL!</p>
<p>As examples:</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">{
    <span class="hljs-string">"_links"</span>: {
        <span class="hljs-string">"self"</span>: {
            <span class="hljs-string">"href"</span>: <span class="hljs-string">"/blog/2014-03-26-apigility-rpc-with-hal"</span>
        }
    },
    <span class="hljs-string">"_embedded"</span>: {
        <span class="hljs-string">"author"</span>: {
            <span class="hljs-string">"_links"</span>: {
                <span class="hljs-string">"self"</span>: {
                    <span class="hljs-string">"href"</span>: <span class="hljs-string">"/blog/author/matthew"</span>
                }
            },
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"matthew"</span>,
            <span class="hljs-string">"name"</span>: <span class="hljs-string">"Matthew Weier O'Phinney"</span>,
            <span class="hljs-string">"url"</span>: <span class="hljs-string">"http://mwop.net"</span>
        },
        <span class="hljs-string">"tags"</span>: [
            {
                <span class="hljs-string">"_links"</span>: {
                    <span class="hljs-string">"self"</span>: {
                        <span class="hljs-string">"href"</span>: <span class="hljs-string">"/blog/tag/php"</span>
                    }
                },
                <span class="hljs-string">"id"</span>: <span class="hljs-string">"php"</span>
            },
            {
                <span class="hljs-string">"_links"</span>: {
                    <span class="hljs-string">"self"</span>: {
                        <span class="hljs-string">"href"</span>: <span class="hljs-string">"/blog/tag/rest"</span>
                    }
                },
                <span class="hljs-string">"id"</span>: <span class="hljs-string">"rest"</span>
            }
        ]
    }
}
</code></pre>
<p>The example above shows two embedded resources. The first is the author; the
second, a collection of tags. Note that <em>every</em> object under <code>_embedded</code> is a
HAL object!</p>
<p>You can go quite far with this — you can also have embedded resources inside
your embedded resources, arbitrarily deep.</p>
</li>
</ul>
<h2>RPC?</h2>
<p>RPC stands for Remote Procedure Call, and, when describing a web API, is
usually used to describe a web service that publishes multiple method calls at
a single URI using only <code>POST</code>; XML-RPC and SOAP are the usual suspects.</p>
<p>In Apigility, we use the term RPC in a much looser sense; we use it to describe
one-off services: actions like &quot;authenticate,&quot; or &quot;notify,&quot; or &quot;register&quot; would
all make sense here. They are actions that usually only need to respond to a
single HTTP method, and which may or may not describe a &quot;thing&quot;, which is what
we usually consider a &quot;resource&quot; when discussing REST terminology.</p>
<p>That said: what if what we want to return from the RPC call <em>are</em> REST
resources?</p>
<h2>Returning HAL from RPC Services</h2>
<p>In order to return HAL from RPC services, we need to understand (a) how Content
Negotiation works, and (b) what needs to be returned in order for the HAL
renderer to be able to create a representation.</p>
<p>For purposes of this example, I'm positing a <code>RegisterController</code> as an RPC
service that, on success, is returning a <code>User</code> object that I want rendered as
a HAL resource.</p>
<p>The <a href="https://github.com/zfcampus/zf-content-negotiation">zf-content-negotiation</a>
module takes care of content negotiation for Apigility. It introspects the
<code>Accept</code> header in order to determine if we can return a representation, and
then, if it can, will cast any <code>ZF\ContentNegotiation\ViewModel</code> returned from
a controller to the appropriate view model for the representation. From there,
a renderer will pick up the view model and do what needs to be done.</p>
<p>So, the first thing we have to do is return <code>ZF\ContentNegotiation\ViewModel</code>
instances from our controller.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">use</span> <span class="hljs-title">Zend</span>\<span class="hljs-title">Mvc</span>\<span class="hljs-title">Controller</span>\<span class="hljs-title">AbstractActionController</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">ZF</span>\<span class="hljs-title">ContentNegotiation</span>\<span class="hljs-title">ViewModel</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegisterController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractActionController</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">registerAction</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-comment">/* ... do some work ... get a user ... */</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ViewModel(<span class="hljs-keyword">array</span>(<span class="hljs-string">'user'</span> =&gt; $user));
    }
}
</code></pre>
<p>The <a href="https://github.com/zfcampus/zf-hal">zf-hal</a> module in Apigility creates
the actual HAL representations. <code>zf-hal</code> looks for a &quot;payload&quot; variable in the
view model, and expects that value to be either a <code>ZF\Hal\Entity</code> (single item)
or <code>ZF\Hal\Collection</code>. When creating an <code>Entity</code> object, you need the object
being represented, as well as the identifier. So, let's update our return
value.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">use</span> <span class="hljs-title">Zend</span>\<span class="hljs-title">Mvc</span>\<span class="hljs-title">Controller</span>\<span class="hljs-title">AbstractActionController</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">ZF</span>\<span class="hljs-title">ContentNegotiation</span>\<span class="hljs-title">ViewModel</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">ZF</span>\<span class="hljs-title">Hal</span>\<span class="hljs-title">Entity</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegisterController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractActionController</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">registerAction</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-comment">/* ... do some work
         * ... get a $user
         * ... assume we have also now have an $id
         */</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ViewModel(<span class="hljs-keyword">array</span>(<span class="hljs-string">'payload'</span> =&gt; <span class="hljs-keyword">array</span>(
            <span class="hljs-string">'user'</span> =&gt; <span class="hljs-keyword">new</span> Entity($user, $id),
        )));
    }
}
</code></pre>
<p><code>zf-hal</code> contains what's called a &quot;metadata map&quot;. This is a map of classes to
information on how <code>zf-hal</code> should render them: what route to use, what
additional relational links to inject, how to serialize the object, what field
represents the identifier, etc.</p>
<p>In most cases, you will have likely already defined a REST service for the
resource you want to return from the RPC service, in which case you will be
done. However, if you want, you can go in and manually configure the metadata
map in your API module's <code>config/module.config.php</code> file:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">return</span> <span class="hljs-keyword">array</span>(
    <span class="hljs-comment">/* ... */</span>
    <span class="hljs-string">'zf-hal'</span> =&gt; <span class="hljs-keyword">array</span>(
        <span class="hljs-string">'metadata_map'</span> =&gt; <span class="hljs-keyword">array</span>(
            <span class="hljs-string">'User'</span> =&gt; <span class="hljs-keyword">array</span>(
                <span class="hljs-string">'route_name'</span> =&gt; <span class="hljs-string">'api.rest.user'</span>,
                <span class="hljs-string">'entity_identifier_name'</span> =&gt; <span class="hljs-string">'username'</span>,
                <span class="hljs-string">'route_identifier_name'</span> =&gt; <span class="hljs-string">'user_id'</span>,
                <span class="hljs-string">'hydrator'</span> =&gt; <span class="hljs-string">'Zend\Stdlib\Hydrator\ObjectProperty'</span>,
            ),
        ),
    ),
);
</code></pre>
<p>Finally, we need to make sure that the service is configured to actually return
HAL. We can do this in the admin if we want. Find the &quot;Content Negotiation&quot;
section of the admin, and the &quot;Content Negotiation Selector&quot; item, and set that
to &quot;HalJson&quot;; don't forget to save! Alternately, you can do this manually in
the API module's <code>config/module.config.php</code> file, under the
<code>zf-content-negotiation</code> section:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">return</span> <span class="hljs-keyword">array</span>(
    <span class="hljs-comment">/* ... */</span>
    <span class="hljs-string">'zf-content-negotiation'</span> =&gt; <span class="hljs-keyword">array</span>(
        <span class="hljs-string">'controllers'</span> =&gt; <span class="hljs-keyword">array</span>(
            <span class="hljs-comment">/* ... */</span>
            <span class="hljs-string">'RegisterController'</span> =&gt; <span class="hljs-string">'HalJson'</span>,
        ),
        <span class="hljs-comment">/* ... */</span>
    ),
);
</code></pre>
<p>Once your changes are complete, when you make a successful request to the URI
for your &quot;register&quot; RPC service, you'll receive a HAL response pointing to the
canonical URI for the user resource created!</p>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/2014-03-26-apigility-rpc-with-hal.html">Apigility: Using RPC with HAL</a> was originally
    published <time class="dt-published" datetime="2014-03-26T15:30:00-05:00">26 March 2014</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>RESTful APIs with ZF2, Part 3</title>
      <pubDate>Mon, 25 Feb 2013 06:29:00 -0600</pubDate>
      <link>https://mwop.net/blog/2013-02-25-restful-apis-with-zf2-part-3.html</link>
      <guid>https://mwop.net/blog/2013-02-25-restful-apis-with-zf2-part-3.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>In my <a href="/blog/2013-02-11-restful-apis-with-zf2-part-1.html">previous</a>
<a href="/blog/2013-02-13-restful-apis-with-zf2-part-2.html">posts</a>, I covered basics
of JSON hypermedia APIs using Hypermedia Application Language (HAL), and
methods for reporting errors, including API-Problem and <code>vnd.error</code>.</p>
<p>In this post, I'll be covering <em>documenting</em> your API — techniques you can use
to indicate what HTTP operations are allowed, as well as convey the full
documentation on what endpoints are available, what they accept, and what you
can expect them to return.</p>
<p>While I will continue covering general aspects of RESTful APIs in this post, I
will also finally introduce several ZF2-specific techniques.</p>


<h2>Why Document?</h2>
<p>If you're asking this question, you've either never consumed software, or your
software is perfect and self-documenting. I frankly don't believe either one.</p>
<p>In the case of APIs, those consuming the API need to know how to use it.</p>
<ul>
<li>What endpoints are available? Which operations are available for each endpoint?</li>
<li>What does each endpoint expect as a payload during the request?</li>
<li>What can you expect as a payload in return?</li>
<li>How will errors be communicated?</li>
</ul>
<p>While the promise of hypermedia APIs is that each response tells you the next
steps available, you still, somewhere along the way, need more information —
what payloads look like, which HTTP verbs should be used, and more. If you're
<strong>not</strong> documenting your API, you're &quot;doing it wrong.&quot;</p>
<h2>Where Should Documentation Live?</h2>
<p>This is the much bigger question.</p>
<p>Of the questions I raised above, detailing what should be documented, there are
two specific types. When discussing what operations are available, we have a
technical solution in the form of the <code>OPTIONS</code> method and its counterpart, the
<code>Allow</code> header. Everything else falls under end-user documentation.</p>
<h2>OPTIONS</h2>
<p>The HTTP specification details the <code>OPTIONS</code> method as idempotent,
non-cacheable, and for use in detailing what operations are available for the
given resource specified by the request URI. It makes specific mention of the
<code>Allow</code> header, but does not limit what is returned for requests made via this
method.</p>
<p>The <code>Allow</code> header details the allowed HTTP methods for the given resource.</p>
<p>Used in combination, you make an <code>OPTIONS</code> request to a URI, and it should
return a response containing an <code>Allow</code> header; from that header value, you
then know what other HTTP methods can be made to that URI.</p>
<p>What this tells us is that our RESTful endpoint should do the following:</p>
<ul>
<li>When an <code>OPTIONS</code> request is made, return a response with an <code>Allow</code> header
that has a list of the available HTTP methods allowed.</li>
<li>For any HTTP method we do <em>not</em> allow, we should return a &quot;405 Not Allowed&quot;
response.</li>
</ul>
<p>These are fairly easy to accomplish in ZF2. <em>(See? I promised I'd get to some
ZF2 code in this post!)</em></p>
<p>When creating RESTful endpoints in ZF2, I recommend using
<code>Zend\Mvc\Controller\AbstractRestfulController</code>. This controller contains an
<code>options()</code> method which you can use to respond to an <code>OPTIONS</code> request. As
with any ZF2 controller, returning a response object will prevent rendering and
bubble out immediately so that the response is returned.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">My</span>\<span class="hljs-title">Controller</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Zend</span>\<span class="hljs-title">Mvc</span>\<span class="hljs-title">Controller</span>\<span class="hljs-title">AbstractRestfulController</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FooController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractRestfulController</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">options</span><span class="hljs-params">()</span>
    </span>{
        $response = <span class="hljs-keyword">$this</span>-&gt;getResponse();
        $headers  = $response-&gt;getHeaders();

        <span class="hljs-comment">// If you want to vary based on whether this is a collection or an</span>
        <span class="hljs-comment">// individual item in that collection, check if an identifier from</span>
        <span class="hljs-comment">// the route is present</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;params()-&gt;fromRoute(<span class="hljs-string">'id'</span>, <span class="hljs-keyword">false</span>)) {
            <span class="hljs-comment">// Allow viewing, partial updating, replacement, and deletion</span>
            <span class="hljs-comment">// on individual items</span>
            $headers-&gt;addHeaderLine(<span class="hljs-string">'Allow'</span>, implode(<span class="hljs-string">','</span>, <span class="hljs-keyword">array</span>(
                <span class="hljs-string">'GET'</span>,
                <span class="hljs-string">'PATCH'</span>,
                <span class="hljs-string">'PUT'</span>,
                <span class="hljs-string">'DELETE'</span>,
            )));
            <span class="hljs-keyword">return</span> $response;
        }

        <span class="hljs-comment">// Allow only retrieval and creation on collections</span>
        $headers-&gt;addHeaderLine(<span class="hljs-string">'Allow'</span>, implode(<span class="hljs-string">','</span>, <span class="hljs-keyword">array</span>(
            <span class="hljs-string">'GET'</span>,
            <span class="hljs-string">'POST'</span>,
        )));
        <span class="hljs-keyword">return</span> $response;
    }
}
</code></pre>
<p>The next trick is returning the 405 response if an invalid option is used. For
this, you can create a listener in your controller, and wire it to listen at
higher-than-default priority. As an example:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">My</span>\<span class="hljs-title">Controller</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Zend</span>\<span class="hljs-title">EventManager</span>\<span class="hljs-title">EventManagerInterface</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Zend</span>\<span class="hljs-title">Mvc</span>\<span class="hljs-title">Controller</span>\<span class="hljs-title">AbstractRestfulController</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FooController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractRestfulController</span>
</span>{
    <span class="hljs-keyword">protected</span> $allowedCollectionMethods = <span class="hljs-keyword">array</span>(
        <span class="hljs-string">'GET'</span>,
        <span class="hljs-string">'POST'</span>,
    );

    <span class="hljs-keyword">protected</span> $allowedResourceMethods = <span class="hljs-keyword">array</span>(
        <span class="hljs-string">'GET'</span>,
        <span class="hljs-string">'PATCH'</span>,
        <span class="hljs-string">'PUT'</span>,
        <span class="hljs-string">'DELETE'</span>,
    );

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setEventManager</span><span class="hljs-params">(EventManagerInterface $events)</span>
    </span>{
        <span class="hljs-keyword">parent</span>::setEventManager($events);
        $events-&gt;attach(<span class="hljs-string">'dispatch'</span>, <span class="hljs-keyword">array</span>(<span class="hljs-keyword">$this</span>, <span class="hljs-string">'checkOptions'</span>), <span class="hljs-number">10</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkOptions</span><span class="hljs-params">($e)</span>
    </span>{
        $matches  = $e-&gt;getRouteMatch();
        $response = $e-&gt;getResponse();
        $request  = $e-&gt;getRequest();
        $method   = $request-&gt;getMethod();

        <span class="hljs-comment">// test if we matched an individual resource, and then test</span>
        <span class="hljs-comment">// if we allow the particular request method</span>
        <span class="hljs-keyword">if</span> ($matches-&gt;getParam(<span class="hljs-string">'id'</span>, <span class="hljs-keyword">false</span>)) {
            <span class="hljs-keyword">if</span> (!in_array($method, <span class="hljs-keyword">$this</span>-&gt;allowedResourceMethods)) {
                $response-&gt;setStatusCode(<span class="hljs-number">405</span>);
                <span class="hljs-keyword">return</span> $response;
            }
            <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-comment">// We matched a collection; test if we allow the particular request </span>
        <span class="hljs-comment">// method</span>
        <span class="hljs-keyword">if</span> (!in_array($method, <span class="hljs-keyword">$this</span>-&gt;allowedCollectionMethods)) {
            $response-&gt;setStatusCode(<span class="hljs-number">405</span>);
            <span class="hljs-keyword">return</span> $response;
        }
    }
}
</code></pre>
<p>Note that I moved the allowed methods into properties; if I did the above, I'd
refactor the <code>options()</code> method to use those properties as well to ensure they
are kept in sync.</p>
<p>Also note that in the case of an invalid method, I return a response object.
This ensures that nothing else needs to execute in the controller; I discover
the problem and return early.</p>
<h2>End-User Documentation</h2>
<p>Now that we have the technical solution out of the way, we're still left with
the bulk of the work left to accomplish: providing end-user documentation
detailing the various payloads, errors, etc.</p>
<p>I've seen two compelling approaches to this problem. The first builds on the
<code>OPTIONS</code> method, and the other uses a hypermedia link in every response to
point to documentation.</p>
<p>The <code>OPTIONS</code> solution is this: <a href="http://zacstewart.com/2012/04/14/http-options-method.html">use the body of an <code>OPTIONS</code> response to provide documentation</a>.
(Keith Casey <a href="https://vimeo.com/49613738">gave an excellent short presentation about this at REST Fest 2012</a>).</p>
<p>The <code>OPTIONS</code> method allows for you to return a body in the response, and also
allows for content negotiation. The theory, then, is that you return
media-type-specific documentation that details the methods allowed, and what
they specifically accept in the body. While there is no standard for this at
this time, the first article I linked suggested including a description, the
parameters expected, and one or more example request bodies for each HTTP
method allowed; you'd likely also want to detail the responses that can be
expected.</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">{
    <span class="hljs-string">"POST"</span>: {
        <span class="hljs-string">"description"</span>: <span class="hljs-string">"Create a new status"</span>,
        <span class="hljs-string">"parameters"</span>: {
            <span class="hljs-string">"type"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"string"</span>,
                <span class="hljs-string">"description"</span>: <span class="hljs-string">"Status type -- text, image, or url; defaults to text"</span>,
                <span class="hljs-string">"required"</span>: <span class="hljs-literal">false</span>
            },
            <span class="hljs-string">"text"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"string"</span>,
                <span class="hljs-string">"description"</span>: <span class="hljs-string">"Status text; required for text types, optional for others"</span>,
                <span class="hljs-string">"required"</span>: <span class="hljs-literal">false</span>
            },
            <span class="hljs-string">"image_url"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"string"</span>,
                <span class="hljs-string">"description"</span>: <span class="hljs-string">"URL of image for image types; required for image types"</span>,
                <span class="hljs-string">"required"</span>: <span class="hljs-literal">false</span>
            },
            <span class="hljs-string">"link_url"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"string"</span>,
                <span class="hljs-string">"description"</span>: <span class="hljs-string">"URL of image for link types; required for link types"</span>,
                <span class="hljs-string">"required"</span>: <span class="hljs-literal">false</span>
            }
        },
        <span class="hljs-string">"responses"</span>: [
            {
                <span class="hljs-string">"describedBy"</span>: <span class="hljs-string">"http://example.com/problems/invalid-status"</span>,
                <span class="hljs-string">"title"</span>: <span class="hljs-string">"Submitted status was invalid"</span>,
                <span class="hljs-string">"detail"</span>: <span class="hljs-string">"Missing text field required for text type"</span>
            },
            {
                <span class="hljs-string">"id"</span>: <span class="hljs-string">"abcdef123456"</span>,
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"text"</span>,
                <span class="hljs-string">"text"</span>: <span class="hljs-string">"This is a status update"</span>,
                <span class="hljs-string">"timestamp"</span>: <span class="hljs-string">"2013-02-22T10:06:05+0:00"</span>
            }
        ],
        <span class="hljs-string">"examples"</span>: [
            {
                <span class="hljs-string">"text"</span>: <span class="hljs-string">"This is a status update"</span>
            },
            {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"image"</span>,
                <span class="hljs-string">"text"</span>: <span class="hljs-string">"This is the image caption"</span>,
                <span class="hljs-string">"image_url"</span>: <span class="hljs-string">"http://example.com/favicon.ico"</span>
            },
            {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"link"</span>,
                <span class="hljs-string">"text"</span>: <span class="hljs-string">"This is a description of the link"</span>,
                <span class="hljs-string">"link_url"</span>: <span class="hljs-string">"http://example.com/"</span>
            },
        ]
    }
}
</code></pre>
<p>If you were to use this methodology, you would alter the <code>options()</code> method
such that it does not return a response object, but instead return a view model
with the documentation.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">My</span>\<span class="hljs-title">Controller</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Zend</span>\<span class="hljs-title">Mvc</span>\<span class="hljs-title">Controller</span>\<span class="hljs-title">AbstractRestfulController</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FooController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractRestfulController</span>
</span>{
    <span class="hljs-keyword">protected</span> $viewModelMap = <span class="hljs-keyword">array</span>(<span class="hljs-comment">/* ... */</span>);

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">options</span><span class="hljs-params">()</span>
    </span>{
        $response = <span class="hljs-keyword">$this</span>-&gt;getResponse();
        $headers  = $response-&gt;getHeaders();

        <span class="hljs-comment">// Get a view model based on Accept types</span>
        $model    = <span class="hljs-keyword">$this</span>-&gt;acceptableViewModelSelector(<span class="hljs-keyword">$this</span>-&gt;viewModelMap);

        <span class="hljs-comment">// If you want to vary based on whether this is a collection or an</span>
        <span class="hljs-comment">// individual item in that collection, check if an identifier from</span>
        <span class="hljs-comment">// the route is present</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;params()-&gt;fromRoute(<span class="hljs-string">'id'</span>, <span class="hljs-keyword">false</span>)) {
            <span class="hljs-comment">// Still set the Allow header</span>
            $headers-&gt;addHeaderLine(<span class="hljs-string">'Allow'</span>, implode(
                <span class="hljs-string">','</span>, 
                <span class="hljs-keyword">$this</span>-&gt;allowedResourceMethods
            ));

            <span class="hljs-comment">// Set documentation specification as variables</span>
            $model-&gt;setVariables(<span class="hljs-keyword">$this</span>-&gt;getResourceDocumentationSpec());
            <span class="hljs-keyword">return</span> $model;
        }

        <span class="hljs-comment">// Allow only retrieval and creation on collections</span>
        $headers-&gt;addHeaderLine(<span class="hljs-string">'Allow'</span>, implode(
            <span class="hljs-string">','</span>,
            <span class="hljs-keyword">$this</span>-&gt;allowedCollectionMethods
        ));
        $model-&gt;setVariables(<span class="hljs-keyword">$this</span>-&gt;getCollectionDocumentationSpec());
        <span class="hljs-keyword">return</span> $model;
    }
}
</code></pre>
<p>I purposely didn't provide the implementations of the
<code>getResourceDocumentationSpec()</code> and <code>getCollectionDocumentationSpec()</code>
methods, as that will likely be highly specific to your application. Another
possibility is to use your view engine for this, and specify a template file
that has the fully-populated information. This would require a custom renderer
when using JSON or XML, but is a pretty easy solution.</p>
<p><strong>However, there's one cautionary tale to tell</strong>, something I already
mentioned: <code>OPTIONS</code>, per the specification, is <em>non-cacheable</em>. What this
means is that everytime somebody makes an <code>OPTIONS</code> request, any cache control
headers you provide will be ignored, which means hitting the server for each
and every request to the documentation. Considering documentation is static,
this is problematic; it has even prompted <a href="http://www.mnot.net/blog/2012/10/29/NO_OPTIONS">blog posts urging you not to use OPTIONS for documentation</a>.</p>
<p>Which brings us to the second solution for end-user documentation: a static
page referenced via a hypermedia link.</p>
<p>This solution is insanely easy: you simply provide a <code>Link</code> header in your
response, and provide a <code>describedby</code> reference pointing to the documentation
page:</p>
<pre><code class="language-http hljs http" data-lang="http"><span class="hljs-attribute">Link</span>: &lt;http://example.com/api/documentation.md&gt;; rel="describedby"
</code></pre>
<p>With ZF2, this is trivially easy to accomplish: create a route and endpoint for
your documentation, and then a listener on your controller that adds the <code>Link</code>
header to your response.</p>
<p>The latter, adding the link header, might look like this:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">My</span>\<span class="hljs-title">Controller</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Zend</span>\<span class="hljs-title">EventManager</span>\<span class="hljs-title">EventManagerInterface</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Zend</span>\<span class="hljs-title">Mvc</span>\<span class="hljs-title">Controller</span>\<span class="hljs-title">AbstractRestfulController</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FooController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractRestfulController</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setEventManager</span><span class="hljs-params">(EventManagerInterface $events)</span>
    </span>{
        <span class="hljs-keyword">parent</span>::setEventManager($events);
        $events-&gt;attach(<span class="hljs-string">'dispatch'</span>, <span class="hljs-keyword">array</span>(<span class="hljs-keyword">$this</span>, <span class="hljs-string">'injectLinkHeader'</span>), <span class="hljs-number">20</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">injectLinkHeader</span><span class="hljs-params">($e)</span>
    </span>{
        $response = $e-&gt;getResponse();
        $headers  = $response-&gt;getHeaders();
        $headers-&gt;addHeaderLine(<span class="hljs-string">'Link'</span>, sprintf(
            <span class="hljs-string">'&lt;%s&gt;; rel="describedby"'</span>, 
            <span class="hljs-keyword">$this</span>-&gt;url(<span class="hljs-string">'documentation-route-name'</span>)
        ));
    }
}
</code></pre>
<p>If you want to ensure you get a fully qualified URL that includes the schema,
hostname, and port, there are a number of ways to do that as well; the above
gives you the basic idea.</p>
<p>Now, for the route and endpoint, there are tools that will help you simplify
that task as well, in the form of a couple of ZF2 modules:
<a href="https://github.com/weierophinney/PhlySimplePage">PhlySimplePage</a> and
<a href="https://github.com/Soflomo/Prototype">Soflomo\Prototype</a>. <em>(Disclosure: I'm
the author of PhlySimplePage.)</em></p>
<p>Both essentially allow you to specify a route and the corresponding template
name to use, which means all you need to do is provide a little configuration,
and a view template. <code>Soflomo\Prototype</code> has slightly simpler configuration, so
I'll demonstrate it here:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">return</span> <span class="hljs-keyword">array</span>(
    <span class="hljs-string">'soflomo_prototype'</span> =&gt; <span class="hljs-keyword">array</span>(
        <span class="hljs-string">'documentation-route-name'</span> =&gt; <span class="hljs-keyword">array</span>(
            <span class="hljs-string">'route'</span>    =&gt; <span class="hljs-string">'/api/documentation'</span>,
            <span class="hljs-string">'template'</span> =&gt; <span class="hljs-string">'api/documentation'</span>,
        ),
    ),
    <span class="hljs-string">'view_manager'</span> =&gt; <span class="hljs-keyword">array</span>(
        <span class="hljs-string">'template_map'</span> =&gt; <span class="hljs-keyword">array</span>(
            <span class="hljs-string">'api/documentation'</span> =&gt; <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/../view/api/documentation.phtml'</span>,
        ),
    ),
);
</code></pre>
<p>I personally have been using the <code>Link</code> header solution, as it's so simple to
implement. It does <em>not</em> write the documentation for you, but thinking about it
early and implementing it helps ensure you at least start writing the
documentation, and, if you open source your project, you may find you have
users who will write the documentation for you if they know where it lives.</p>
<h2>Conclusions</h2>
<p>Document your API, or either nobody will use it, or all you're hear are
complaints from your users about having to guess constantly about how to use
it. Include the following information:</p>
<ul>
<li>What endpoint(s) is (are) available.</li>
<li>Which operations are available for each endpoint.
<ul>
<li>What payloads are expected by the endpoint.</li>
<li>What payloads can a user expect in return.</li>
<li>What media types may be used for requests.</li>
<li>What media types may be expected in responses.</li>
</ul>
</li>
</ul>
<p>Additionally, make sure that you do the <code>OPTIONS</code>/<code>Allow</code> dance; don't just
accept any request method, and report the standard 405 response for methods
that you will not allow. Make sure you differentiate these for collections
versus individual resources, as you likely may allow replacing or updating an
individual resource, but likely will not want to do the same for a whole
collection!</p>
<h2>Next time</h2>
<p>So far, I've covered the basics of RESTful JSON APIS, specifically recommending
Hypermedia Application Language (HAL) for providing hypermedia linking and
relations. I've covered error reporting, and provided two potential formats
(API-Problem and vnd.error) for use with your APIs. Now, in this article, I've
shown a bit about documenting your API both for machine consumption as well as
end-users. What's left?</p>
<p>In upcoming parts, I'll talk about ZF2's <code>AbstractRestfulController</code> in more
detail, as well as how to perform some basic content negotiation. I've also had
requests about how one might deal with API versioning, and will attempt to
demonstrate some techniques for doing that as well. Finally, expect to see a
post showing how I've tied all of this together in a general-purpose ZF2 module
so that you can ignore all of these posts and simply start writing APIs.</p>
<h3>Updates</h3>
<p><em>Note: I'll update this post with links to the other posts in the series as I
publish them.</em></p>
<ul>
<li><a href="/blog/2013-02-11-restful-apis-with-zf2-part-1.html">Part 1</a></li>
<li><a href="/blog/2013-02-13-restful-apis-with-zf2-part-2.html">Part 2</a></li>
</ul>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/2013-02-25-restful-apis-with-zf2-part-3.html">RESTful APIs with ZF2, Part 3</a> was originally
    published <time class="dt-published" datetime="2013-02-25T06:29:00-06:00">25 February 2013</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>RESTful APIs with ZF2, Part 2</title>
      <pubDate>Wed, 13 Feb 2013 07:40:00 -0600</pubDate>
      <link>https://mwop.net/blog/2013-02-13-restful-apis-with-zf2-part-2.html</link>
      <guid>https://mwop.net/blog/2013-02-13-restful-apis-with-zf2-part-2.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>In my <a href="/blog/2013-02-11-restful-apis-with-zf2-part-1.html">last post</a>, I
covered some background on REST and the Richardson Maturity Model, and some
emerging standards around hypermedia APIs in JSON; in particular, I outlined
aspects of Hypermedia Application Language (HAL), and how it can be used to
define a generic structure for JSON resources.</p>
<p>In this post, I cover an aspect of RESTful APIs that's often overlooked:
reporting problems.</p>


<h2>Background</h2>
<p>APIs are useful when they're working. But when they fail, they're only useful
if they provide us with meaningful information; if all I get is a status code,
and no indication of what caused the issue, or where I might look for more
information, I get frustrated.</p>
<p>In consuming APIs, I've come to the following conclusions:</p>
<ul>
<li>Error conditions need to provide detailed information as to what went wrong,
and what steps I may be able to take next. An error code with no context
gives me nothing to go on.</li>
<li>Errors need to be reported consistently. Don't report the error one way one
time, and another way the next.</li>
<li><strong>DO</strong> use HTTP status codes to indicate an error happened. Nothing is more
irksome than getting back a 200 status with an error payload.</li>
<li>Errors should be reported in a format I have indicated I will Accept (as in
the HTTP header). Perhaps the only think more irksome than a 200 status code
for an error is getting back an HTML page when I expect JSON.</li>
</ul>
<h2>Why Status Codes Aren't Enough</h2>
<p>Since REST leverages and builds on HTTP, an expedient solution for reporting
problems is to simply use <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP status codes</a>.
These are well understood by web developers, right?</p>
<p><code>4xx</code> error codes are errors made by the requestor, and are actually fairly
reasonable to use for reporting things such as lack of authorization tokens,
incomplete requests, unsupportable operations, or non-supported media types.</p>
<p>But what happens when the error is on the server — because something has gone
wrong such as inability to reach your persistence layer or credential storage?
The <code>5xx</code> series of status codes is sparse and wholly unsuited to reporting
errors of these types — <em>though you'll likely still want to use a <code>500</code> status
to report the failure</em>. But what do you present to the consumer so that they
know whether or not to try again, or what to report to you so that you can fix
the issue?</p>
<p>A status code simply isn't enough information most of the time. Yes, you want
to define standard status codes so that your clients can perform reasonable
branching, but you also need a way to communicate <em>details</em> to the end-user, so
that they can log the information for themselves, display information to their
own end-users, and/or report it back to you so you can do something to resolve
the situation.</p>
<h2>Custom Media Types</h2>
<p>The first step is to use a custom media type. Media types are typically both a
name as well as a structure — and the latter is what we're after when it comes
to error reporting.</p>
<p>If we return a response using this media type, the client then knows how to
parse it, and can then process it, log it, whatever.</p>
<p>Sure, you can make up your own format — as long as you are consistent in using
it, and you document it. But personally, I don't like inventing new formats
when standard formats exist already. Custom formats mean that custom clients
are required for working with the services; using a standard format can save
effort and time.</p>
<p>In the world of JSON, I've come across two error media types that appear to be
gaining traction: <code>application/api-problem+json</code> and
<code>application/vnd.error+json</code></p>
<h3>API-Problem</h3>
<p>This particular media type is <a href="http://tools.ietf.org/html/draft-nottingham-http-problem-02">via the IETF</a>.
Like HAL, it provides formats in both JSON and XML, making it a nice
cross-platform choice.</p>
<p>As noted already, the media type is <code>application/api-problem+json</code>. The
representation is a single resource, with the following properties:</p>
<ul>
<li><strong>describedBy</strong>: a URL to a document describing the error condition (required)</li>
<li><strong>title</strong>: a brief title for the error condition (required)</li>
<li><strong>httpStatus</strong>: the HTTP status code for the current request (optional)</li>
<li><strong>detail</strong>: error details specific to this request (optional)</li>
<li><strong>supportId</strong>: a URL to the specific problem occurrence (e.g., to a log message) (optional)</li>
</ul>
<p>As an example:</p>
<pre><code class="language-http hljs http" data-lang="http">HTTP/1.1 <span class="hljs-number">500</span> Internal Error
<span class="hljs-attribute">Content-Type</span>: application/api-problem+json

{
    "describedBy": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html",
    "detail": "Status failed validation",
    "httpStatus": 500,
    "title": "Internal Server Error"
}
</code></pre>
<p>The specification allows a large amount of flexibility — you can have your own
custom error types, so long as you have a description of them to link to. You
can provide as little or as much detail as you want, and even decide what
information to expose based on environment.</p>
<p>I personally like to point to the HTTP status code definitions, and then
provide request-specific detail; I find this gives quick and simple results
that I can later shape as I add more detail to my API. However, the
specification definitely encourages you to have unique error types with
discrete URIs that describe them — never a bad thing when creating APIs.</p>
<h3>vnd.error</h3>
<p>This is a <a href="https://github.com/blongden/vnd.error">proposed media type</a> within
the HAL community. Like HAL, it provides formats in both JSON and XML, making
it a nice cross-platform choice.</p>
<p>It differentiates from API-Problem in a few ways. First, it allows, and even
encourages, reporting collections of errors. If you consider PHP exceptions and
the fact that they support &quot;previous&quot; exceptions, this is a powerful concept;
you can report the entire chain of errors that led to the response. Second, it
encourages pushing detail out of the web service; errors include a &quot;logRef&quot;
property that points to where the error detail lives. This is probably better
illustrated than explained.</p>
<p>The response payload is an array of objects. Each object has the following
members:</p>
<ul>
<li><strong>logRef</strong>: a unique identifier for the specific error which can then be used
to identify the error within server-side logs (required)</li>
<li><strong>message</strong>: the error message itself (required)</li>
<li><strong>_links</strong>: HAL-compatible links. Typically, &quot;help&quot;, &quot;describes&quot;, and/or
&quot;describedBy&quot; relations will be defined here.</li>
</ul>
<p>As an example, let's consider the API-Problem example I had earlier, and
provide a <code>vnd.error</code> equivalent:</p>
<pre><code class="language-http hljs http" data-lang="http">HTTP/1.1 <span class="hljs-number">500</span> Internal Error
<span class="hljs-attribute">Content-Type</span>: application/vnd.error+json

[
    {
        "logRef": "someSha1HashMostLikely",
        "message": "Status failed validation",
        "_links": {
            "describedBy": {"href": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html"}
        }
    }
]
</code></pre>
<p><code>vnd.error</code> basically begs you to create custom error types, with documentation
end-points that detail the source of the error and what you can do about it
(this is true of API-Problem as well).</p>
<p>The requirement to include a log reference (&quot;logRef&quot;) and have it be unique can
be a stumbling block to implementation, however, as it requires effort for
uniquely identifying requests, and logging. However, both the identification
and logging can be automated.</p>
<h2>Summary</h2>
<p>Error reporting in APIs is as important as the normal resource payloads
themselves. Without good error reporting, when an API raises errors, clients
have difficulty understanding what they can do next, and cannot provide you,
the API provider, with information that will allow you to debug on the server
side.</p>
<p>As noted at the beginning of the article, if you follow the rules below, you'll
make consumers of your API happier and more productive.</p>
<ul>
<li><strong>DO</strong> use appropriate HTTP status codes to indicate an error happened.</li>
<li>Report errors in a format I have indicated I will Accept (as in the HTTP
header).</li>
<li>Report errors consistently. Don't report the error one way one time, and
another way the next. Standardize on a specific error-reporting media type .
While you <em>can</em> create your own error structure, I recommend using
documented, accepted standards. This will make clients more re-usable, and
make many of your decisions for you.</li>
<li>Provide detailed information as to what went wrong, and what steps I may be
able to take next. Provide documentation for each type of error, and link to
that documentation from your error payloads.</li>
</ul>
<p>Which brings me to…</p>
<h2>Next time</h2>
<p>I realize I still haven't covered anything specific to ZF2, but I'll start next
time, when I cover the next topic: documenting your API. An undocumented API is
a useless API, so it's good to start baking documentation in immediately. I'll
survey some of the possibilities and how they can be implemented in ZF2 in the
next installment, and then we can get our hands dirty with actual API
development.</p>
<h3>Updates</h3>
<p><em>Note: I'll update this post with links to the other posts in the series as I
publish them.</em></p>
<ul>
<li><a href="/blog/2013-02-11-restful-apis-with-zf2-part-1.html">Part 1</a></li>
<li><a href="/blog/2013-02-25-restful-apis-with-zf2-part-3.html">Part 3</a></li>
</ul>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/2013-02-13-restful-apis-with-zf2-part-2.html">RESTful APIs with ZF2, Part 2</a> was originally
    published <time class="dt-published" datetime="2013-02-13T07:40:00-06:00">13 February 2013</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>RESTful APIs with ZF2, Part 1</title>
      <pubDate>Wed, 13 Feb 2013 07:40:00 -0600</pubDate>
      <link>https://mwop.net/blog/2013-02-11-restful-apis-with-zf2-part-1.html</link>
      <guid>https://mwop.net/blog/2013-02-11-restful-apis-with-zf2-part-1.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>RESTful APIs have been an interest of mine for a couple of years, but due to
<a href="http://framework.zend.com/blog//zend-framework-2-0-0-stable-released.html">circumstances</a>,
I've not had much chance to work with them in any meaningful fashion until
recently.</p>
<p><a href="http://akrabat.com/">Rob Allen</a> and I proposed a workshop for
<a href="http://conference.phpbenelux.eu/2013/">PHP Benelux 2013</a> covering RESTful APIs
with ZF2.  When it was accepted, it gave me the perfect opportunity to dive in
and start putting the various pieces together.</p>


<h2>Background</h2>
<p>I've attended any number of conference sessions on API design, read countless
articles, and engaged in quite a number of conversations. Three facts keep
cropping up:</p>
<ol>
<li>JSON is fast becoming the preferred exchange format due to the ease with
which it de/serializes in almost every language.</li>
<li>The &quot;holy grail&quot; is <a href="http://martinfowler.com/articles/richardsonMaturityModel.html">Richardson Maturity Model</a> Level 3.</li>
<li>It's really hard to achieve RMM level 3 with JSON.</li>
</ol>
<h3>Richardson Maturity Model</h3>
<p>As a quick review, the Richardson Maturity Model has the following 4 levels:</p>
<ul>
<li>Level 0: &quot;The swamp of POX.&quot; Basically, a service that uses TCP for
transport, primarily as a form of remote procedure call (RPC). Typically,
these are not really leveraging HTTP in any meaningful fashion; most systems
will use HTTP POST for all interactions. Also, you will often have a single
endpoint for all interactions, regardless of whether or not they are strictly
related. XML-RPC, SOAP, and JSON-RPC fall under this category.</li>
<li>Level 1: &quot;Resources.&quot; In these services, you start breaking the service into
multiple services, one per &quot;resource,&quot; or, in object oriented terms, per
object. This means a distinct URL per object, which means each has its own
distinct identity on the web; this often extends not only to the collection
of objects, but to individual objects under the collection as well (e.g.,
<code>/books</code> as well as <code>/books/life-of-pi</code>). The service may still be RPC in
nature, however, and, at this level, often is still using a single HTTP
method for all interactions with the resource.</li>
<li>Level 2: &quot;HTTP Verbs.&quot; At this level, we start using HTTP verbs with our
services in the way the HTTP specification intends. GET is for safe
operations, and should be cacheable; POST is used for creation and/or
updating; DELETE can be used to delete a resource; etc. Rather than doing RPC
style methods, we leverage HTTP, occasionally passing additional parameters
via the query string or request body. Considerations such as HTTP caching and
idempotence are taken into account.</li>
<li>Level 3: &quot;Hypermedia Controls.&quot; Building on the previous level, our resource
representations now also include <em>links</em>, which indicate what we can <em>do
next</em>. At this level, our API becomes practically self-describing; given a
single end-point, we should be able to start crawling it, using the links in
a representation to lead us to the next actions.</li>
</ul>
<p>When I first started playing with web services around a decade ago, everything
was stuck at Level 0 or Level 1 — usually with Level 1 users downgrading to
Level 0 because Level 0 offerred consistency and predictability if you chose to
use a service type that had a defined envelope format (such as XML-RPC or
SOAP). (I even wrote the XML-RPC server implementation for Zend Framework
because I got sick of writing one-off parsers/serializers for custom XML web
service implementations. When you're implementing many services, predictability
is a huge win.)</p>
<p>A few years ago, I started seeing a trend towards Level 2. Web developers like
the simplicity of using HTTP verbs, as they map very well to
<a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> operations
— the bread and butter of web development. Couple this concept with JSON, and
it becomes trivially simple to both create a web service, as well as consume
it.</p>
<p><em>I'd argue that the majority of web developers are quite happy to be at Level 2
— and have no problem staying there. They're productive, and the concepts are
easy — both to understand and to implement.</em></p>
<p>Level 3, though, is where it becomes really interesting. The idea that I can
examine the represention <em>alone</em> in order to understand what I can do next is
very intriguing and empowering.</p>
<h3>JSON and Hypermedia</h3>
<p>With XML, hypermedia basically comes for free. Add some <code>&lt;link&gt;</code> elements to
your representation, and you're done — and don't forget the link <code>rel</code>ations!</p>
<p>JSON, however, is another story.</p>
<p>Where do the links go? <em>There is no single, defined way to represent a
hyperlink in JSON.</em></p>
<p>Fortunately, there are some emerging standards.</p>
<p>First is use of the <a href="http://www.w3.org/wiki/LinkHeader">&quot;Link&quot; HTTP header</a>.
While the page I linked shows only a single link in the header, you can have
multiple links separated by commas. GitHub uses this when providing pagination
links in their API. Critics will point out that the HTTP headers are not
technically part of the representation, however; strict interpetations of REST
and RMM indicate that the hypermedia links should be part of the resource
representation. Regardless, having the links in the HTTP headers is useful for
pre-traversal of a service, as you can perform HEAD requests only to discover
possible actions and workflows.</p>
<p><a href="http://amundsen.com/media-types/collection/format/">Collection+JSON</a> is
interesting, as it describes the entire JSON envelope. My one criticism is that
it details too much; whenever I see a format that dictates how to describe
types, I think of XML-RPC or SOAP, and get a little twitchy. It's definitely
worth a look, though.</p>
<p>What's captured my attention of late, however, is
<a href="http://stateless.co/hal_specification.html">Hypertext Application Language</a>,
or HAL for short. HAL has very few rules, but succinctly describes both how to
provide hypermedia in JSON as well as how to represent embedded resources — the
two things that most need standardized structure in JSON. It does this while
still providing a generic media type, and also describing a mirror image XML
format!</p>
<h3>HAL Media Types</h3>
<p>HAL defines two generic media types: <code>application/hal+xml</code> and
<code>application/hal+json</code>. You will use these as the response <code>Content-Type</code>, as
they describe the response representation; the client can simply request
<code>application/json</code>, and the response format remains compatible.</p>
<h3>HAL and Links</h3>
<p>HAL provides a very simple structure for JSON hypermedia links. First, all
resource representations must contain hypermedia links, and all links are
provided in a <code>_links</code> object:</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">{
    <span class="hljs-string">"_links"</span>: {
    }
}
</code></pre>
<p>Second, links are properties of this object. The property name is the link
relation, and the value is an object containing minimally an &quot;href&quot; property.</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">{
    <span class="hljs-string">"_links"</span>: {
        <span class="hljs-string">"self"</span>: {<span class="hljs-string">"href"</span>: <span class="hljs-string">"http://example.com/api/status/1234"</span>}
    }
}
</code></pre>
<p>If a given relation can have multiple links, you provide instead an array of objects:</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">{
    <span class="hljs-string">"_links"</span>: {
        <span class="hljs-string">"self"</span>: {<span class="hljs-string">"href"</span>: <span class="hljs-string">"http://example.com/api/status/1234"</span>},
        <span class="hljs-string">"conversation"</span>: [
            {<span class="hljs-string">"href"</span>: <span class="hljs-string">"http://example.com/api/status/1237"</span>},
            {<span class="hljs-string">"href"</span>: <span class="hljs-string">"http://example.com/api/status/1241"</span>}
        ]
    }
}
</code></pre>
<p>Individual links can contain other attributes as desired — I've seen people
include the relation again so that it's self-contained in the link object, and
it's not uncommon to include a title or name.</p>
<h3>HAL and Resources</h3>
<p>HAL imposes no structure over resources other than requiring the hypermedia
links; even then, you typically do not include the hypermedia links when making
a request of the web service; the hypermedia links are included only in the
representations <em>returned</em> by the service.</p>
<p>So, as an example, you would POST the following:</p>
<pre><code class="language-http hljs http" data-lang="http"><span class="hljs-attribute">POST /api/status
Host</span>: example.com
<span class="hljs-attribute">Accept</span>: application/json
<span class="hljs-attribute">Content-Type</span>: application/json

{
    "status": "This is my awesome status update!",
    "user": "mwop"
}
</code></pre>
<p>And from that request, you'd receive the following:</p>
<pre><code class="language-http hljs http" data-lang="http"><span class="hljs-attribute">201 Created
Location</span>: http://example.com/api/status/1347
<span class="hljs-attribute">Content-Type</span>: application/hal+json

{
    "_links": {
        "self": {"href": "http://example.com/api/status/1347"}
    },
    "id": "1347",
    "timestamp": "2013-02-11 23:33:47",
    "status": "This is my awesome status update!",
    "user": "mwop"
}
</code></pre>
<h3>HAL and Embedded Resources</h3>
<p>The other important thing that HAL defines is how to <em>embed</em> resources. Why is
this important? If the resource references other resources, you will want to be
able to link to them so you can perform operations on them, too.</p>
<p>Embedded resources are represented inside an <code>_embedded</code> object of the
representation, and, as resources, contain their own <code>_links</code> object as well.
Each resource you embed is assigned to a property of that object, and if
multiple objects of the same type are returned, an array of resources is
assigned. In fact, this latter is how you represent <em>collections</em> in HAL.</p>
<p>Let's consider a simple example first. In previous code samples, I have a
&quot;user&quot; that's a string; let's make that an embedded resource instead.</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">{
    <span class="hljs-string">"_links"</span>: {
        <span class="hljs-string">"self"</span>: {<span class="hljs-string">"href"</span>: <span class="hljs-string">"http://example.com/api/status/1347"</span>}
    },
    <span class="hljs-string">"id"</span>: <span class="hljs-string">"1347"</span>,
    <span class="hljs-string">"timestamp"</span>: <span class="hljs-string">"2013-02-11 23:33:47"</span>,
    <span class="hljs-string">"status"</span>: <span class="hljs-string">"This is my awesome status update!"</span>,
    <span class="hljs-string">"_embedded"</span>: {
        <span class="hljs-string">"user"</span>: {
            <span class="hljs-string">"_links"</span>: {
                <span class="hljs-string">"self"</span>: {<span class="hljs-string">"href"</span>: <span class="hljs-string">"http://example.com/api/user/mwop"</span>}
            }
            <span class="hljs-string">"id"</span>: <span class="hljs-string">"mwop"</span>,
            <span class="hljs-string">"name"</span>: <span class="hljs-string">"Matthew Weier O'Phinney"</span>,
            <span class="hljs-string">"url"</span>: <span class="hljs-string">"http://mwop.net"</span>
        }
    }
}
</code></pre>
<p>I've moved the &quot;user&quot; out of the representation, and into the <code>_embedded</code>
object — because this is where you define embedded resources. Note that the
&quot;user&quot; is a standard HAL resource itself — containing hypermedia links.</p>
<p>Now let's look at a collection:</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">{
    <span class="hljs-string">"_links"</span>: {
        <span class="hljs-string">"self"</span>: {<span class="hljs-string">"href"</span>: <span class="hljs-string">"http://example.com/api/status"</span>},
        <span class="hljs-string">"next"</span>: {<span class="hljs-string">"href"</span>: <span class="hljs-string">"http://example.com/api/status?page=2"</span>},
        <span class="hljs-string">"last"</span>: {<span class="hljs-string">"href"</span>: <span class="hljs-string">"http://example.com/api/status?page=100"</span>}
    },
    <span class="hljs-string">"count"</span>: <span class="hljs-number">2973</span>,
    <span class="hljs-string">"per_page"</span>: <span class="hljs-number">30</span>,
    <span class="hljs-string">"page"</span>: <span class="hljs-number">1</span>,
    <span class="hljs-string">"_embedded"</span>: {
        <span class="hljs-string">"status"</span>: [
            {
                <span class="hljs-string">"_links"</span>: {
                    <span class="hljs-string">"self"</span>: {<span class="hljs-string">"href"</span>: <span class="hljs-string">"http://example.com/api/status/1347"</span>}
                },
                <span class="hljs-string">"id"</span>: <span class="hljs-string">"1347"</span>,
                <span class="hljs-string">"timestamp"</span>: <span class="hljs-string">"2013-02-11 23:33:47"</span>,
                <span class="hljs-string">"status"</span>: <span class="hljs-string">"This is my awesome status update!"</span>,
                <span class="hljs-string">"_embedded"</span>: {
                    <span class="hljs-string">"user"</span>: {
                        <span class="hljs-string">"_links"</span>: {
                            <span class="hljs-string">"self"</span>: {<span class="hljs-string">"href"</span>: <span class="hljs-string">"http://example.com/api/user/mwop"</span>}
                        }
                        <span class="hljs-string">"id"</span>: <span class="hljs-string">"mwop"</span>,
                        <span class="hljs-string">"name"</span>: <span class="hljs-string">"Matthew Weier O'Phinney"</span>,
                        <span class="hljs-string">"url"</span>: <span class="hljs-string">"http://mwop.net"</span>
                    }
                }
            }
            <span class="hljs-comment">/* ... */</span>
        ]
    }
}
</code></pre>
<p>Note that the &quot;status&quot; property is an array; semantically, all resources under
this key are of the same type. Also note that the parent resource has some
additional link relations — these are related to pagination, and allow a
client to determine what the next and last pages are (and, if we were midway
into the collection, previous and first pages). Since the collection is also a
resource, it has some interesting metadata — how many resources are in the
collection, how many we represent per page, and what the current page is.</p>
<p>Also note that you can nest resources — simply include an <code>_embedded</code> object
inside an embedded resource, with additional resources, as I've done with the
&quot;user&quot; resource inside the status resource shown here. It's turtles all the way
down.</p>
<h2>Next Time</h2>
<p>The title of this post indicates I'll be talking about building RESTful APIs
with ZF2 — but so far, I've not said anything about ZF2.</p>
<p>I'll get there. But there's another detour to take: reporting errors.</p>
<h3>Updates</h3>
<p><em>Note: I'll update this post with links to the other posts in the series as I
publish them.</em></p>
<ul>
<li><a href="/blog/2013-02-13-restful-apis-with-zf2-part-2.html">Part 2</a></li>
<li><a href="/blog/2013-02-25-restful-apis-with-zf2-part-3.html">Part 3</a></li>
</ul>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/2013-02-11-restful-apis-with-zf2-part-1.html">RESTful APIs with ZF2, Part 1</a> was originally
    published <time class="dt-published" datetime="2013-02-12T05:42:00-06:00">12 February 2013</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>Responding to Different Content Types in RESTful ZF Apps</title>
      <pubDate>Wed, 10 Mar 2010 09:28:22 -0600</pubDate>
      <link>https://mwop.net/blog/233-Responding-to-Different-Content-Types-in-RESTful-ZF-Apps.html</link>
      <guid>https://mwop.net/blog/233-Responding-to-Different-Content-Types-in-RESTful-ZF-Apps.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>In <a href="/blog/227-Exposing-Service-APIs-via-Zend-Framework.html">previous</a>
<a href="/blog/228-Building-RESTful-Services-with-Zend-Framework.html">articles</a>, I've
explored building service endpoints and RESTful services with Zend Framework.
With RPC-style services, you get to cheat: the protocol dictates the content
type (XML-RPC uses XML, JSON-RPC uses JSON, SOAP uses XML, etc.). With REST,
however, you have to make choices: what serialization format will you support?</p>
<p>Why not support multiple formats?</p>
<p>There's no reason you can't re-use your RESTful web service to support multiple
formats. Zend Framework and PHP have plenty of tools to assist you in responding
to different format requests, so don't limit yourself. With a small amount of
work, you can make your controllers format agnostic, and ensure that you respond
appropriately to different requests.</p>


<h2>Content-Type Detection</h2>
<p>The first problem to solve is going to be how to retrieve passed parameters.
When using XML or JSON as your serialization format, you aren't getting your
standard POST variables — you're getting a raw post instead, and you'll need to
deserialize the payload. In fact, if you're getting a PUT request, you also have
some work to do, as PHP doesn't do anything with PUT requests.</p>
<p>I do this via an action helper. The basic algorithm is:</p>
<ul>
<li>Do we have a raw body in the request? If not, nothing more need be done.</li>
<li>Determine the Content-Type passed in the request headers, and decode appropriately:
<ul>
<li>If it was JSON, pass the raw request body to <code>json_decode</code> or <code>Zend_Json::decode</code>.</li>
<li>If it was XML, I pass the raw request body to the <code>Zend_Config_XML</code> constructor, and then serialize to an arrya using the <code>toArray()</code> method. Yes, it's a hack, but it's effective.</li>
<li>Otherwise, I assume I've got a regular PUT-style request, and I pass the data to <code>parse_str()</code>.</li>
</ul>
</li>
</ul>
<p>I keep the values within the action helper, and then retrieve them on demand
within my action controller. The helper looks like the following:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Scrummer_Controller_Helper_Params</span> 
    <span class="hljs-keyword">extends</span> <span class="hljs-title">Zend_Controller_Action_Helper_Abstract</span>
</span>{
    <span class="hljs-comment">/**
     * <span class="hljs-doctag">@var</span> array Parameters detected in raw content body
     */</span>
    <span class="hljs-keyword">protected</span> $_bodyParams = <span class="hljs-keyword">array</span>();

    <span class="hljs-comment">/**
     * Do detection of content type, and retrieve parameters from raw body if 
     * present
     * 
     * <span class="hljs-doctag">@return</span> void
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">init</span><span class="hljs-params">()</span>
    </span>{
        $request     = <span class="hljs-keyword">$this</span>-&gt;getRequest();
        $contentType = $request-&gt;getHeader(<span class="hljs-string">'Content-Type'</span>);
        $rawBody     = $request-&gt;getRawBody();
        <span class="hljs-keyword">if</span> (!$rawBody) {
            <span class="hljs-keyword">return</span>;
        }
        <span class="hljs-keyword">switch</span> (<span class="hljs-keyword">true</span>) {
            <span class="hljs-keyword">case</span> (strstr($contentType, <span class="hljs-string">'application/json'</span>)):
                <span class="hljs-keyword">$this</span>-&gt;setBodyParams(Zend_Json::decode($rawBody));
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> (strstr($contentType, <span class="hljs-string">'application/xml'</span>)):
                $config = <span class="hljs-keyword">new</span> Zend_Config_Xml($rawBody);
                <span class="hljs-keyword">$this</span>-&gt;setBodyParams($config-&gt;toArray());
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">default</span>:
                <span class="hljs-keyword">if</span> ($request-&gt;isPut()) {
                    parse_str($rawBody, $params);
                    <span class="hljs-keyword">$this</span>-&gt;setBodyParams($params);
                }
                <span class="hljs-keyword">break</span>;
        }
    }

    <span class="hljs-comment">/**
     * Set body params
     * 
     * <span class="hljs-doctag">@param</span>  array $params 
     * <span class="hljs-doctag">@return</span> Scrummer_Controller_Action
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setBodyParams</span><span class="hljs-params">(array $params)</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;_bodyParams = $params;
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>;
    }

    <span class="hljs-comment">/**
     * Retrieve body parameters
     * 
     * <span class="hljs-doctag">@return</span> array
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getBodyParams</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;_bodyParams;
    }

    <span class="hljs-comment">/**
     * Get body parameter
     * 
     * <span class="hljs-doctag">@param</span>  string $name 
     * <span class="hljs-doctag">@return</span> mixed
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getBodyParam</span><span class="hljs-params">($name)</span>
    </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;hasBodyParam($name)) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;_bodyParams[$name];
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }

    <span class="hljs-comment">/**
     * Is the given body parameter set?
     * 
     * <span class="hljs-doctag">@param</span>  string $name 
     * <span class="hljs-doctag">@return</span> bool
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hasBodyParam</span><span class="hljs-params">($name)</span>
    </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">isset</span>(<span class="hljs-keyword">$this</span>-&gt;_bodyParams[$name])) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }

    <span class="hljs-comment">/**
     * Do we have any body parameters?
     * 
     * <span class="hljs-doctag">@return</span> bool
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hasBodyParams</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">empty</span>(<span class="hljs-keyword">$this</span>-&gt;_bodyParams)) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }

    <span class="hljs-comment">/**
     * Get submit parameters
     * 
     * <span class="hljs-doctag">@return</span> array
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSubmitParams</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;hasBodyParams()) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;getBodyParams();
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;getRequest()-&gt;getPost();
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">direct</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;getSubmitParams();
    }
}
</code></pre>
<p>This helper is intended to be run on each request, so I register it in my
bootstrap:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bootstrap</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Zend_Application_Bootstrap_Bootstrap</span>
</span>{
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_initActionHelpers</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-comment">// ...</span>
        $params = <span class="hljs-keyword">new</span> Scrummer_Controller_Helper_Params();
        Zend_Controller_Action_HelperBroker::addHelper($params);
        <span class="hljs-comment">// ...</span>
    }
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Within your action controller, all you need to do is call the helper:</p>
<pre><code class="language-php hljs php" data-lang="php">$data = <span class="hljs-keyword">$this</span>-&gt;params();
</code></pre>
<p>In a RESTful controller, you'll only need to use this with your <code>postAction</code> and
<code>putAction</code>. The beauty is that your controller can remain ignorant of the
Content-Type — you write the same logic to retrieve your parameters regardless.</p>
<h2>Responding to the client: Context Switching</h2>
<p>So, the first half of the problem is taken care of: how to handle the request.
The second half is responding appropriately.</p>
<p>Zend Framework has some built in tooling to help with this. The ContextSwitch
and AjaxContext action helpers look for a particular parameter — &quot;format&quot; by
default — and, if detected, will render an alternate view script named after the
context. As an example, if an &quot;XML&quot; context is detected, it will render
<code>&lt;controller&gt;/&lt;action&gt;.xml.phtml</code> — note the <code>.xml</code> segment of the script name.</p>
<p>Both helpers work in the same basic way (the latter, AjaxContext, will only
activate if the request is determined to originate from an XMLHttpRequest): you
define which actions in the controller are context sensitive, and then if the
context is detected, a new view script will be used.</p>
<p>So, the first trick is ensuring that the context is passed. As mentioned before,
the helpers look for a &quot;format&quot; parameter in the request object. You can pass
this using a query parameter — <code>?format=xml</code> — but I find that ugly. There's an
HTTP header defined for this purpose already: &quot;Accept&quot;.</p>
<p>Detecting the header and injecting the context into the request is absurdly
simple, and can be done in a <code>dispatchLoopStartup</code> plugin:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Scrummer_Controller_Plugin_AcceptHandler</span>
    <span class="hljs-keyword">extends</span> <span class="hljs-title">Zend_Controller_Plugin_Abstract</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">dispatchLoopStartup</span><span class="hljs-params">(Zend_Controller_Request_Abstract $request)</span>
    </span>{
        <span class="hljs-keyword">if</span> (!$request <span class="hljs-keyword">instanceof</span> Zend_Controller_Request_Http) {
            <span class="hljs-keyword">return</span>;
        }

        $header = $request-&gt;getHeader(<span class="hljs-string">'Accept'</span>);
        <span class="hljs-keyword">switch</span> (<span class="hljs-keyword">true</span>) {
            <span class="hljs-keyword">case</span> (strstr($header, <span class="hljs-string">'application/json'</span>)):
                $request-&gt;setParam(<span class="hljs-string">'format'</span>, <span class="hljs-string">'json'</span>);
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> (strstr($header, <span class="hljs-string">'application/xml'</span>) 
                  &amp;&amp; (!strstr($header, <span class="hljs-string">'html'</span>))):
                $request-&gt;setParam(<span class="hljs-string">'format'</span>, <span class="hljs-string">'xml'</span>);
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">default</span>:
                <span class="hljs-keyword">break</span>;
        }
    }
}
</code></pre>
<p>The above can be registered in your application configuration:</p>
<pre><code class="language-ini hljs ini" data-lang="ini"><span class="hljs-attr">resources.frontController.plugins[]</span> = <span class="hljs-string">"Scrummer_Controller_Plugin_AcceptHandler"</span>
</code></pre>
<p>I like my RESTful controllers to automatically expose their methods as
context-aware. To make this happen, I defined a marker interface,
<code>Scrummer_Rest_Controller</code>, and created an action helper that checks if the
current controller implements it; if it does, I then automatically add contexts
for the RESTful actions.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Scrummer_Controller_Helper_RestContexts</span>
    <span class="hljs-keyword">extends</span> <span class="hljs-title">Zend_Controller_Action_Helper_Abstract</span>
</span>{
    <span class="hljs-keyword">protected</span> $_contexts = <span class="hljs-keyword">array</span>(
        <span class="hljs-string">'xml'</span>, 
        <span class="hljs-string">'json'</span>,
    );

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">preDispatch</span><span class="hljs-params">()</span>
    </span>{
        $controller = <span class="hljs-keyword">$this</span>-&gt;getActionController();
        <span class="hljs-keyword">if</span> (!$controller <span class="hljs-keyword">instanceof</span> Scrummer_Rest_Controller) {
            <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-keyword">$this</span>-&gt;_initContexts();

        <span class="hljs-comment">// Set a Vary response header based on the Accept header</span>
        <span class="hljs-keyword">$this</span>-&gt;getResponse()-&gt;setHeader(<span class="hljs-string">'Vary'</span>, <span class="hljs-string">'Accept'</span>);
    }

    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_initContexts</span><span class="hljs-params">()</span>
    </span>{
        $cs = <span class="hljs-keyword">$this</span>-&gt;getActionController()-&gt;contextSwitch;
        $cs-&gt;setAutoJsonSerialization(<span class="hljs-keyword">false</span>);
        <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">$this</span>-&gt;_contexts <span class="hljs-keyword">as</span> $context) {
            <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">array</span>(<span class="hljs-string">'index'</span>, <span class="hljs-string">'post'</span>, <span class="hljs-string">'get'</span>, <span class="hljs-string">'put'</span>, <span class="hljs-string">'delete'</span>) <span class="hljs-keyword">as</span> $action) {
                $cs-&gt;addActionContext($action, $context);
            }
        }
        $cs-&gt;initContext();
    }
}
</code></pre>
<p>Register this via the bootstrap as well:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bootstrap</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Zend_Application_Bootstrap_Bootstrap</span>
</span>{
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_initActionHelpers</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-comment">// ...</span>
        $params = <span class="hljs-keyword">new</span> Scrummer_Controller_Helper_Params();
        Zend_Controller_Action_HelperBroker::addHelper($params);

        $contexts = <span class="hljs-keyword">new</span> Scrummer_Controller_Helper_RestContexts();
        Zend_Controller_Action_HelperBroker::addHelper($contexts);
        <span class="hljs-comment">// ...</span>
    }
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>There are two things to note about this helper. First, you'll see that I specify
a &quot;Vary&quot; header. This is to ensure that if the client chooses to cache
responses, it will cache separate responses based on the value sent in the
&quot;Accept&quot; header.</p>
<p>Second, note that I turn off automatic JSON serialization in the ContextSwitch
helper. I do this so that I can keep my controller context agnostic; this will
require additional view scripts, but the ability to keep my controller logic
simple will be worth it. More on that in a moment.</p>
<p>We now have the infrastructure in place to respond to different contexts based
on the &quot;Accept&quot; header, and can retrieve parameters appropriately based on the
&quot;Content-Type&quot; provided us. Now comes the actual response.</p>
<h2>Responding to the client: Views</h2>
<p>Recall that ContextSwitch will attach an additional prefix to the specified view
script — <code>&lt;controller&gt;/&lt;action&gt;.phtml</code> will become
<code>&lt;controller&gt;/&lt;action&gt;.xml.phtml</code> or <code>&lt;controller&gt;/&lt;action&gt;.json.phtml</code>.
Basically, for each context we will respond to, we have an additional view
script per action.</p>
<pre><code class="language-lua hljs lua" data-lang="lua">views/
|<span class="hljs-comment">-- scripts/</span>
|   `<span class="hljs-comment">-- foo/</span>
|      |<span class="hljs-comment">-- delete.phtml</span>
|      |<span class="hljs-comment">-- delete.json.phtml</span>
|      |<span class="hljs-comment">-- delete.xml.phtml</span>
|      |<span class="hljs-comment">-- get.phtml</span>
|      |<span class="hljs-comment">-- get.json.phtml</span>
|      |<span class="hljs-comment">-- get.xml.phtml</span>
|      |<span class="hljs-comment">-- index.phtml</span>
|      |<span class="hljs-comment">-- index.json.phtml</span>
|      |<span class="hljs-comment">-- index.xml.phtml</span>
|      |<span class="hljs-comment">-- post.phtml</span>
|      |<span class="hljs-comment">-- post.json.phtml</span>
|      |<span class="hljs-comment">-- post.xml.phtml</span>
|      |<span class="hljs-comment">-- put.phtml</span>
|      |<span class="hljs-comment">-- put.json.phtml</span>
|      `<span class="hljs-comment">-- put.xml.phtml</span>
</code></pre>
<p>This may seem like overkill, but consider the following representative method
from my controller:</p>
<pre><code class="language-php hljs php" data-lang="php">    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postAction</span><span class="hljs-params">()</span>
    </span>{
        $data    = <span class="hljs-keyword">$this</span>-&gt;params();
        $service = <span class="hljs-keyword">$this</span>-&gt;getService();
        $result  = $service-&gt;add($data);  
        <span class="hljs-keyword">if</span> (!$result) {
            <span class="hljs-keyword">$this</span>-&gt;view-&gt;form = $service-&gt;getBacklogForm();
            <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-keyword">$this</span>-&gt;view-&gt;success = <span class="hljs-keyword">true</span>;
        <span class="hljs-keyword">$this</span>-&gt;view-&gt;backlog = $result;
    }
</code></pre>
<p>You don't see anything in there about headers, redirects, or XHR requests. Just
slinging data to services and views. Real simple.</p>
<p>The view scripts then take care of the appropriate display logic. Let's look at
two view scripts for the above action, one for plain old HTML, the other for a
JSON response:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-meta">&lt;?php</span> <span class="hljs-comment">// backlog/post.phtml <span class="hljs-meta">?&gt;</span></span>
<span class="hljs-meta">&lt;?php</span> 
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;success):
    <span class="hljs-keyword">$this</span>-&gt;response-&gt;setRedirect(<span class="hljs-keyword">$this</span>-&gt;url(<span class="hljs-keyword">array</span>(
        <span class="hljs-string">'controller'</span> =&gt; <span class="hljs-string">'backlog'</span>,
        <span class="hljs-string">'id'</span>         =&gt; <span class="hljs-keyword">$this</span>-&gt;backlog-&gt;id,
    ), <span class="hljs-string">'rest'</span>, <span class="hljs-keyword">true</span>));
<span class="hljs-keyword">else</span>: <span class="hljs-meta">?&gt;</span>
&lt;h2&gt;Create <span class="hljs-keyword">new</span> backlog&lt;/h2&gt;
<span class="hljs-meta">&lt;?php</span>
    <span class="hljs-keyword">$this</span>-&gt;form-&gt;setAction(<span class="hljs-keyword">$this</span>-&gt;url())
               -&gt;setMethod(<span class="hljs-string">'post'</span>);
    <span class="hljs-keyword">echo</span> <span class="hljs-keyword">$this</span>-&gt;form;
<span class="hljs-keyword">endif</span> <span class="hljs-meta">?&gt;</span>

<span class="hljs-meta">&lt;?php</span> <span class="hljs-comment">// backlog/post.json.phtml <span class="hljs-meta">?&gt;</span></span>
<span class="hljs-meta">&lt;?php</span>
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;success) {
    $url = <span class="hljs-keyword">$this</span>-&gt;url(<span class="hljs-keyword">array</span>(
        <span class="hljs-string">'controller'</span> =&gt; <span class="hljs-string">'backlog'</span>,
        <span class="hljs-string">'id'</span>         =&gt; <span class="hljs-keyword">$this</span>-&gt;backlog-&gt;id,
    ), <span class="hljs-string">'rest'</span>, <span class="hljs-keyword">true</span>);
    <span class="hljs-keyword">$this</span>-&gt;response-&gt;setHeader(<span class="hljs-string">'Location'</span>, $url)
                   -&gt;setHttpResponseCode(<span class="hljs-number">201</span>);
    <span class="hljs-keyword">echo</span> <span class="hljs-keyword">$this</span>-&gt;json(<span class="hljs-keyword">$this</span>-&gt;backlog-&gt;toArray());
    <span class="hljs-keyword">return</span>;
}

$form = <span class="hljs-keyword">$this</span>-&gt;form;
$form-&gt;setAction(<span class="hljs-keyword">$this</span>-&gt;url())
     -&gt;setMethod(<span class="hljs-string">'post'</span>);
<span class="hljs-keyword">echo</span> <span class="hljs-keyword">$this</span>-&gt;jsonFormErrors($form);
</code></pre>
<p>A few things to note: I inject my response object into the view. I feel HTTP
headers are part of the view, and thus I deal with them there. That also serves
the purpose of keeping my controllers thin and agnostic. Additionally, you'll
note that I use different response codes for HTML versus JSON — this allows my
JSON-REST support to be RESTful, by returning a 201 status code indicating the
resource was created; I also return a JSON representation of the object.
Finally, you'll note that I have a special view helper for creating JSON
representations of validation errors.</p>
<h2>Closing points</h2>
<p>This post is far from exhaustive, and I expect it will likely raise at least as
many questions as it tries to answer.</p>
<p>My main point in this article is to get you, the reader and developer, thinking
creatively about how to expose RESTful web services. Hopefully, you're taking
the following away:</p>
<ol>
<li>Architect in such a way as to minimize the code in your controllers; keep that code as agnostic as possible in regards to where input comes from and what type of response is required.</li>
<li>Use front controller plugins and action helpers to create scaffolding for your services; these are incredibly flexible and re-usable, and help make point 1 that much easier.</li>
<li>Offload as much as possible to your views. This will allow you to isolate logic specific to given formats.</li>
</ol>
<p>What are you waiting for? Don't you have an API to expose?</p>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/233-Responding-to-Different-Content-Types-in-RESTful-ZF-Apps.html">Responding to Different Content Types in RESTful ZF Apps</a> was originally
    published <time class="dt-published" datetime="2010-03-04T15:28:07-06:00">4 March 2010</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>Building RESTful Services with Zend Framework</title>
      <pubDate>Wed, 11 Nov 2009 10:38:41 -0600</pubDate>
      <link>https://mwop.net/blog/228-Building-RESTful-Services-with-Zend-Framework.html</link>
      <guid>https://mwop.net/blog/228-Building-RESTful-Services-with-Zend-Framework.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>As a followup to my <a href="/blog/227-Exposing-Service-APIs-via-Zend-Framework.html">previous post</a>,
I now turn to RESTful web services. I originally encountered the term when attending
php|tropics in 2005, where <a href="http://twitter.com/g_schlossnagle">George Schlossnaggle</a>
likened it to simple GET and POST requests. Since then, the architectural style
— and developer understanding of the architectural style — has improved a bit,
and a more solid definition can be made.</p>


<p>At its heart,
<a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">REST</a> simply
dictates that a given resource have a unique address, and that you interact with
that resource using HTTP verbs. The standard verbs utilized are:</p>
<ul>
<li><em>GET</em>: retrieve a list of resources, or, if an identifier is present, view a single resource</li>
<li><em>POST</em>: create a new resource with the data provided in the POST</li>
<li><em>PUT</em>: update an existing resource as specified by an identifier, using the PUT data</li>
<li><em>DELETE</em>: delete an existing resource as specified by an identifier</li>
</ul>
<p>The standard URL structure used is as follows:</p>
<ul>
<li><code>/resource</code> - GET (list) and POST operations</li>
<li><code>/resource/{identifier}</code> - GET (view), PUT, and DELETE operations</li>
</ul>
<p>What the REST paradigm provides you is a simple, standard way to structure your
CRUD (Create-Read-Update-Delete) applications. Due to the large number of REST
clients available, it also means that if you follow the rules, you get a ton of
interoperability with those clients.</p>
<p>As of <a href="http://framework.zend.com/">Zend Framework</a> 1.9.0, it's trivially easy to
create RESTful routes for your MVC application, as well as to handle the various
REST actions via action controllers.</p>
<p><a href="http://framework.zend.com/manual/en/zend.controller.router.html#zend.controller.router.routes.rest">Zend_Rest_Route</a>
allows you to define RESTful controllers at several levels:</p>
<ul>
<li>You can make it the default route, meaning that unless you have additional
routes, all controllers will be considered REST controllers.</li>
<li>You can specify modules that contain RESTful controllers.</li>
<li>You can specify specific controllers per module that are RESTful</li>
</ul>
<p>As examples:</p>
<pre><code class="language-php hljs php" data-lang="php">$front = Zend_Controller_Front::getInstance();
$router = $front-&gt;getRouter();

<span class="hljs-comment">// Specifying all controllers as RESTful:</span>
$restRoute = <span class="hljs-keyword">new</span> Zend_Rest_Route($front);
$router-&gt;addRoute(<span class="hljs-string">'default'</span>, $restRoute);

<span class="hljs-comment">// Specifying the "api" module only as RESTful:</span>
$restRoute = <span class="hljs-keyword">new</span> Zend_Rest_Route($front, <span class="hljs-keyword">array</span>(), <span class="hljs-keyword">array</span>(
    <span class="hljs-string">'api'</span>,
));
$router-&gt;addRoute(<span class="hljs-string">'rest'</span>, $restRoute);

<span class="hljs-comment">// Specifying the "api" module as RESTful, and the "task" controller of the</span>
<span class="hljs-comment">// "backlog" module as RESTful:</span>
$restRoute = <span class="hljs-keyword">new</span> Zend_Rest_Route($front, <span class="hljs-keyword">array</span>(), <span class="hljs-keyword">array</span>(
    <span class="hljs-string">'api'</span>,
    <span class="hljs-string">'backlog'</span> =&gt; <span class="hljs-keyword">array</span>(<span class="hljs-string">'task'</span>),
));
$router-&gt;addRoute(<span class="hljs-string">'rest'</span>, $restRoute);
</code></pre>
<p>To define a RESTful action controller, you can either extend
<code>Zend_Rest_Controller</code>, or simply define the following methods in a standard
controller extending <code>Zend_Controller_Action</code> (you'll need to define them
regardless):</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-comment">// Or extend Zend_Rest_Controller</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RestController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Zend_Controller_Action</span>
</span>{
    <span class="hljs-comment">// Handle GET and return a list of resources</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">indexAction</span><span class="hljs-params">()</span> </span>{}

    <span class="hljs-comment">// Handle GET and return a specific resource item</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAction</span><span class="hljs-params">()</span> </span>{}

    <span class="hljs-comment">// Handle POST requests to create a new resource item</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postAction</span><span class="hljs-params">()</span> </span>{}

    <span class="hljs-comment">// Handle PUT requests to update a specific resource item</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">putAction</span><span class="hljs-params">()</span> </span>{}

    <span class="hljs-comment">// Handle DELETE requests to delete a specific item</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deleteAction</span><span class="hljs-params">()</span> </span>{}
}
</code></pre>
<p>For those methods that operate on individual resources (<code>getAction()</code>,
<code>putAction()</code>, and <code>deleteAction()</code>), you can test for the identifier using the
following:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">if</span> (!$id = <span class="hljs-keyword">$this</span>-&gt;_getParam(<span class="hljs-string">'id'</span>, <span class="hljs-keyword">false</span>)) {
    <span class="hljs-comment">// report error, redirect, etc.</span>
}
</code></pre>
<h2>Responding is an art</h2>
<p>Many developers are either unaware of or ignore the part of the specification
that dictates what the <em>response</em> should look like.</p>
<p>For instance, in classic REST, after performing a POST to create a new item, you
should do the following:</p>
<ul>
<li>Set the HTTP response code to 201, indicating &quot;Created&quot;</li>
<li>Set the Location header to point to the canonical URI for the newly created item: <code>/team/31</code></li>
<li>Provide a representation of the newly created item</li>
</ul>
<p>Note that there's no redirect, which flies in the face of standard web
development (where GET-POST-Redirect is the typical format). This is a common
&quot;gotcha&quot; moment.</p>
<p>Similarly, with PUT requests, you simply indicate an HTTP 200 status when
successful, and show a representation of the updated item. DELETE requests
should return an HTTP 204 status (indicating success - no content), with no body
content.</p>
<p><em>Note: when building RESTful HTML applications, you may want to still do GET-POST-Redirect to prevent caching issues. The above applies to RESTful web services, which typically use XML or JSON for transactions, and have smart clients for interacting with the service.</em></p>
<p>I'll be writing another article soon showing some tips and tricks for
interacting with HTTP headers, both from the request and for the response, as
it's a subject lengthy enough for a post of its own. In the meantime, start
playing with <code>Zend_Rest_Route</code> and standardizing on it for your CRUD operations!</p>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/228-Building-RESTful-Services-with-Zend-Framework.html">Building RESTful Services with Zend Framework</a> was originally
    published <time class="dt-published" datetime="2009-11-09T09:00:00-06:00">9 November 2009</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
  </channel>
</rss>
