mirror of
https://github.com/almet/notmyidea.git
synced 2025-04-28 11:32:39 +02:00
198 lines
No EOL
15 KiB
HTML
198 lines
No EOL
15 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<title>
|
|
Introducing Cornice - Alexis Métaireau </title>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link rel="stylesheet"
|
|
href="https://blog.notmyidea.org/theme/css/main.css?v2"
|
|
type="text/css" />
|
|
<link href="https://blog.notmyidea.org/feeds/all.atom.xml"
|
|
type="application/atom+xml"
|
|
rel="alternate"
|
|
title="Alexis Métaireau ATOM Feed" />
|
|
</head>
|
|
<body>
|
|
<div id="content">
|
|
<section id="links">
|
|
<ul>
|
|
<li>
|
|
<a class="main" href="/">Alexis Métaireau</a>
|
|
</li>
|
|
<li>
|
|
<a class=""
|
|
href="https://blog.notmyidea.org/journal/index.html">Journal</a>
|
|
</li>
|
|
<li>
|
|
<a class="selected"
|
|
href="https://blog.notmyidea.org/code/">Code, etc.</a>
|
|
</li>
|
|
<li>
|
|
<a class=""
|
|
href="https://blog.notmyidea.org/weeknotes/">Notes hebdo</a>
|
|
</li>
|
|
<li>
|
|
<a class=""
|
|
href="https://blog.notmyidea.org/lectures/">Lectures</a>
|
|
</li>
|
|
<li>
|
|
<a class=""
|
|
href="https://blog.notmyidea.org/projets.html">Projets</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<header>
|
|
<h1 class="post-title">Introducing Cornice</h1>
|
|
<time datetime="2011-12-07T00:00:00+01:00">07 décembre 2011</time>
|
|
</header>
|
|
<article>
|
|
|
|
<p>Wow, already my third working day at Mozilla. Since Monday, I’ve been
|
|
working with <a href="http://ziade.org">Tarek Ziadé</a>, on a pyramid <span class="caps">REST</span>-ish
|
|
toolkit named <a href="https://github.com/mozilla-services/cornice">Cornice</a>.</p>
|
|
<p>Its goal is to take care for you of what you’re usually missing so you
|
|
can focus on what’s important. Cornice provides you facilities for
|
|
validation of any kind.</p>
|
|
<p>The goal is to simplify your work, but we don’t want to reinvent the
|
|
wheel, so it is easily pluggable with validations frameworks, such as
|
|
<a href="http://docs.pylonsproject.org/projects/colander/en/latest/">Colander</a>.</p>
|
|
<h2 id="handling-errors-and-validation">Handling errors and validation</h2>
|
|
<p>Here is how it works:</p>
|
|
<div class="highlight"><pre><span></span><code><span class="n">service</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">"service"</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s2">"/service"</span><span class="p">)</span>
|
|
|
|
|
|
<span class="k">def</span> <span class="nf">is_awesome</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="s1">'awesome'</span> <span class="ow">in</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="p">:</span>
|
|
<span class="n">request</span><span class="o">.</span><span class="n">errors</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s1">'query'</span><span class="p">,</span> <span class="s1">'awesome'</span><span class="p">,</span>
|
|
<span class="s1">'the awesome parameter is required'</span><span class="p">)</span>
|
|
|
|
|
|
<span class="nd">@service</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">validator</span><span class="o">=</span><span class="n">is_awesome</span><span class="p">)</span>
|
|
<span class="k">def</span> <span class="nf">get1</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
|
<span class="k">return</span> <span class="p">{</span><span class="s2">"test"</span><span class="p">:</span> <span class="s2">"yay!"</span><span class="p">}</span>
|
|
</code></pre></div>
|
|
|
|
<p>All the errors collected during the validation process, or after, are
|
|
collected before returning the request. If any, a error 400 is fired up,
|
|
with the list of problems encountered returned as a nice json list
|
|
response (we plan to support multiple formats in the future)</p>
|
|
<p>As you might have seen, request.errors.add takes three parameters:
|
|
<strong>location</strong>, <strong>name</strong> and <strong>description</strong>.</p>
|
|
<p><strong>location</strong> is where the error is located in the request. It can either
|
|
be “body”, “query”, “headers” or “path”. <strong>name</strong> is the name of the
|
|
variable causing problem, if any, and <strong>description</strong> contains a more
|
|
detailed message.</p>
|
|
<p>Let’s run this simple service and send some queries to it:</p>
|
|
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>curl<span class="w"> </span>-v<span class="w"> </span>http://127.0.0.1:5000/service
|
|
><span class="w"> </span>GET<span class="w"> </span>/service<span class="w"> </span>HTTP/1.1
|
|
><span class="w"> </span>Host:<span class="w"> </span><span class="m">127</span>.0.0.1:5000
|
|
><span class="w"> </span>Accept:<span class="w"> </span>*/*
|
|
>
|
|
*<span class="w"> </span>HTTP<span class="w"> </span><span class="m">1</span>.0,<span class="w"> </span>assume<span class="w"> </span>close<span class="w"> </span>after<span class="w"> </span>body
|
|
<<span class="w"> </span>HTTP/1.0<span class="w"> </span><span class="m">400</span><span class="w"> </span>Bad<span class="w"> </span>Request
|
|
<<span class="w"> </span>Content-Type:<span class="w"> </span>application/json<span class="p">;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>UTF-8
|
|
<span class="o">[{</span><span class="s2">"location"</span>:<span class="w"> </span><span class="s2">"query"</span>,<span class="w"> </span><span class="s2">"name"</span>:<span class="w"> </span><span class="s2">"awesome"</span>,<span class="w"> </span><span class="s2">"description"</span>:<span class="w"> </span><span class="s2">"You lack awesomeness!"</span><span class="o">}</span>
|
|
</code></pre></div>
|
|
|
|
<p>I’ve removed the extra clutter from the curl’s output, but you got the
|
|
general idea.</p>
|
|
<p>The content returned is in <span class="caps">JSON</span>, and I know exactly what I have to do:
|
|
add an “awesome” parameter in my query. Let’s do it again:</p>
|
|
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>curl<span class="w"> </span>http://127.0.0.1:5000/service?awesome<span class="o">=</span>yeah
|
|
<span class="o">{</span><span class="s2">"test"</span>:<span class="w"> </span><span class="s2">"yay!"</span><span class="o">}</span>
|
|
</code></pre></div>
|
|
|
|
<p>Validators can also convert parts of the request and store the converted
|
|
value in request.validated. It is a standard dict automatically attached
|
|
to the requests.</p>
|
|
<p>For instance, in our validator, we can chose to validate the parameter
|
|
passed and use it in the body of the webservice:</p>
|
|
<div class="highlight"><pre><span></span><code><span class="n">service</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">"service"</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s2">"/service"</span><span class="p">)</span>
|
|
|
|
|
|
<span class="k">def</span> <span class="nf">is_awesome</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="s1">'awesome'</span> <span class="ow">in</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="p">:</span>
|
|
<span class="n">request</span><span class="o">.</span><span class="n">errors</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s1">'query'</span><span class="p">,</span> <span class="s1">'awesome'</span><span class="p">,</span>
|
|
<span class="s1">'the awesome parameter is required'</span><span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">request</span><span class="o">.</span><span class="n">validated</span><span class="p">[</span><span class="s1">'awesome'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'awesome '</span> <span class="o">+</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="p">[</span><span class="s1">'awesome'</span><span class="p">]</span>
|
|
|
|
|
|
<span class="nd">@service</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">validator</span><span class="o">=</span><span class="n">is_awesome</span><span class="p">)</span>
|
|
<span class="k">def</span> <span class="nf">get1</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
|
<span class="k">return</span> <span class="p">{</span><span class="s2">"test"</span><span class="p">:</span> <span class="n">request</span><span class="o">.</span><span class="n">validated</span><span class="p">[</span><span class="s1">'awesome'</span><span class="p">]}</span>
|
|
</code></pre></div>
|
|
|
|
<p>The output would look like this:</p>
|
|
<div class="highlight"><pre><span></span><code>curl http://127.0.0.1:5000/service?awesome=yeah
|
|
{"test": "awesome yeah"}
|
|
</code></pre></div>
|
|
|
|
<h2 id="dealing-with-accept-headers">Dealing with “Accept” headers</h2>
|
|
<p>The <span class="caps">HTTP</span> spec defines a <strong>Accept</strong> header the client can send so the
|
|
response is encoded the right way. A resource, available at an <span class="caps">URL</span>, can
|
|
be available in different formats. This is especially true for web services.</p>
|
|
<p>Cornice can help you dealing with this. The services you define can tell
|
|
which Content-Type values they can deal with and this will be checked
|
|
against the <strong>Accept</strong> headers sent by the client.</p>
|
|
<p>Let’s refine a bit our previous example, by specifying which
|
|
content-types are supported, using the accept parameter:</p>
|
|
<div class="highlight"><pre><span></span><code><span class="nd">@service</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">validator</span><span class="o">=</span><span class="n">is_awesome</span><span class="p">,</span> <span class="n">accept</span><span class="o">=</span><span class="p">(</span><span class="s2">"application/json"</span><span class="p">,</span> <span class="s2">"text/json"</span><span class="p">))</span>
|
|
<span class="k">def</span> <span class="nf">get1</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
|
<span class="k">return</span> <span class="p">{</span><span class="s2">"test"</span><span class="p">:</span> <span class="s2">"yay!"</span><span class="p">}</span>
|
|
</code></pre></div>
|
|
|
|
<p>Now, if you specifically ask for <span class="caps">XML</span>, Cornice will throw a 406 with the
|
|
list of accepted Content-Type values:</p>
|
|
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>curl<span class="w"> </span>-vH<span class="w"> </span><span class="s2">"Accept: application/xml"</span><span class="w"> </span>http://127.0.0.1:5000/service
|
|
><span class="w"> </span>GET<span class="w"> </span>/service<span class="w"> </span>HTTP/1.1
|
|
><span class="w"> </span>Host:<span class="w"> </span><span class="m">127</span>.0.0.1:5000
|
|
><span class="w"> </span>Accept:<span class="w"> </span>application/xml
|
|
>
|
|
<<span class="w"> </span>HTTP/1.0<span class="w"> </span><span class="m">406</span><span class="w"> </span>Not<span class="w"> </span>Acceptable
|
|
<<span class="w"> </span>Content-Type:<span class="w"> </span>application/json<span class="p">;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>UTF-8
|
|
<<span class="w"> </span>Content-Length:<span class="w"> </span><span class="m">33</span>
|
|
<
|
|
<span class="o">[</span><span class="s2">"application/json"</span>,<span class="w"> </span><span class="s2">"text/json"</span><span class="o">]</span>
|
|
</code></pre></div>
|
|
|
|
<h2 id="building-your-documentation-automatically">Building your documentation automatically</h2>
|
|
<p>writing documentation for web services can be painful, especially when
|
|
your services evolve. Cornice provides a sphinx directive to
|
|
automatically document your <span class="caps">API</span> in your docs.</p>
|
|
<div class="highlight"><pre><span></span><code><span class="nx">rst</span>
|
|
<span class="p">..</span><span class="w"> </span><span class="nx">services</span><span class="o">::</span>
|
|
<span class="w"> </span><span class="p">:</span><span class="kn">package</span><span class="p">:</span><span class="w"> </span><span class="nx">coolapp</span>
|
|
<span class="w"> </span><span class="p">:</span><span class="nx">service</span><span class="p">:</span><span class="w"> </span><span class="nx">quote</span>
|
|
</code></pre></div>
|
|
|
|
<p>Here is an example of what a generated page looks like:
|
|
<a href="http://packages.python.org/cornice/exampledoc.html">http://packages.python.org/cornice/exampledoc.html</a></p>
|
|
<h2 id="yay-how-can-i-get-it">Yay! How can I get it?</h2>
|
|
<p>We just cut a 0.4 release, so it’s available at
|
|
<a href="http://pypi.python.org/pypi/cornice">http://pypi.python.org/pypi/cornice</a> You can install it easily using
|
|
pip, for instance:</p>
|
|
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>cornice
|
|
</code></pre></div>
|
|
|
|
<p>You can also have a look at the documentation at
|
|
<a href="http://packages.python.org/cornice/">http://packages.python.org/cornice/</a></p>
|
|
<h2 id="whats-next">What’s next?</h2>
|
|
<p>We try to make our best to find how Cornice can help you build better
|
|
web services. Cool features we want for the future include the automatic
|
|
publication of a static definition of the services, so it can be used by
|
|
clients to discover services in a nice way.</p>
|
|
<p>Of course, we are open to all your ideas and patches! If you feel
|
|
haskish and want to see the sources, <a href="https://github.com/mozilla-services/cornice">go grab them on
|
|
github</a> , commit and send
|
|
us a pull request!</p>
|
|
</article>
|
|
<footer>
|
|
<a id="feed" href="/feeds/all.atom.xml">
|
|
<img alt="RSS Logo" src="/theme/rss.svg" />
|
|
</a>
|
|
</footer>
|
|
</div>
|
|
</body>
|
|
</html> |