mirror of
https://github.com/almet/notmyidea.git
synced 2025-04-28 19:42:37 +02:00
153 lines
No EOL
9.6 KiB
HTML
153 lines
No EOL
9.6 KiB
HTML
<!DOCTYPE HTML>
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
|
<link rel="stylesheet" href="./theme/css/main.css" type="text/css" media="screen" charset="utf-8">
|
|
<link href="./feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Alexis' log ATOM Feed" />
|
|
<title>Alexis Métaireau</title>
|
|
</head>
|
|
<body>
|
|
<div id="top">
|
|
<p class="author"><a href=".">Alexis Métaireau</a>'s thougths</p>
|
|
<ul class="links"></ul>
|
|
</div>
|
|
<div class="content clear">
|
|
<h1>Introducing cornice</h1>
|
|
<p class="date">Published on Tue 06 December 2011</p>
|
|
<p>Wow, already my third working day at mozilla. Since Monday, I've been working with
|
|
Tarek Ziadé, on a pyramid REST-ish toolkit named <a class="reference external" href="https://github.com/mozilla-services/cornice">cornice</a>.</p>
|
|
<p>Its goal is to take all the hard bits appart from you when implementing a web
|
|
service, 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 Collander.</p>
|
|
<div class="section" id="handling-errors-and-validation">
|
|
<h2>Handling errors and validation</h2>
|
|
<p>We have changed the way errors are handled. Here is how it works:</p>
|
|
<div class="highlight"><pre><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="s">"service"</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s">"/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="s">'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="s">'body'</span><span class="p">,</span> <span class="s">'awesome'</span><span class="p">,</span>
|
|
<span class="s">'the awesome parameter is required'</span><span class="p">)</span>
|
|
|
|
|
|
<span class="nd">@service.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="s">"test"</span><span class="p">:</span> <span class="s">"yay!"</span><span class="p">}</span>
|
|
</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 encoutred encoded as a nice json list (we plan to support multiple
|
|
formats in the future)</p>
|
|
<p>As you might have seen, <cite>request.errors.add</cite> takes three parameters: <strong>location</strong>,
|
|
<strong>name</strong> and <strong>description</strong>.</p>
|
|
<p><strong>location</strong> is where the error arised. 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 detailled message.</p>
|
|
<p>Let's run this simple service, with <cite>bin/paster serve</cite> and send some queries to
|
|
it:</p>
|
|
<div class="highlight"><pre>$ curl -v http://127.0.0.1:5000/service
|
|
> GET /service HTTP/1.1
|
|
> Host: 127.0.0.1:5000
|
|
> Accept: */*
|
|
>
|
|
* HTTP 1.0, assume close after body
|
|
< HTTP/1.0 400 Bad Request
|
|
< Content-Type: application/json; charset=UTF-8
|
|
[{"location": "body", "name": "awesome", "description": "You lack awesomeness!"}
|
|
</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 JSON, and I know exactly what I have to do: add an
|
|
"awesome" parameter in my query. Let's do it again:</p>
|
|
<pre class="literal-block">
|
|
$ curl http://127.0.0.1:5000/service?awesome=yeah
|
|
{"test": "yay!"}
|
|
</pre>
|
|
<p>Validators can also attach extra information about validations to the request,
|
|
using <cite>request.validated</cite>. 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 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="s">"service"</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s">"/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="s">'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="s">'body'</span><span class="p">,</span> <span class="s">'awesome'</span><span class="p">,</span>
|
|
<span class="s">'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="s">'awesome'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'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="s">'awesome'</span><span class="p">]</span>
|
|
|
|
|
|
<span class="nd">@service.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="s">"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="s">'awesome'</span><span class="p">]}</span>
|
|
</pre></div>
|
|
<pre class="literal-block">
|
|
curl http://127.0.0.1:5000/service?awesome=yeah
|
|
{"test": "awesome yeah"}
|
|
</pre>
|
|
</div>
|
|
<div class="section" id="dealing-with-accept-headers">
|
|
<h2>Dealing with "Accept" headers</h2>
|
|
<p>The HTTP spec defines a <strong>Accept</strong> header the client can send so the response
|
|
is encoded the right way. A resource, available at an URL, can be available in
|
|
different formats. This is especially true for web services.</p>
|
|
<p>Cornice can help you to deal with this. The services you define can tell which
|
|
content-types 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 <cite>accept</cite> parameter:</p>
|
|
<div class="system-message">
|
|
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">./content/mozilla/introducing-cornice.rst</tt>, line 117)</p>
|
|
<p>Error in "code-block" directive:
|
|
1 argument(s) required, 0 supplied.</p>
|
|
<pre class="literal-block">
|
|
.. code-block::
|
|
|
|
@service.get(validator=is_awesome, accept=("application/json", "text/json"))
|
|
def get1(request):
|
|
return {"test": "yay!"}
|
|
|
|
</pre>
|
|
</div>
|
|
<p>Now, if you specifically ask for XML, for instance, cornice will throw a 406
|
|
with the list of accepted content-types:</p>
|
|
<pre class="literal-block">
|
|
$ curl -vH "Accept: application/xml" http://127.0.0.1:5000/service
|
|
> GET /service HTTP/1.1
|
|
> Host: 127.0.0.1:5000
|
|
> Accept: application/xml
|
|
>
|
|
< HTTP/1.0 406 Not Acceptable
|
|
< Content-Type: application/json; charset=UTF-8
|
|
< Content-Length: 33
|
|
<
|
|
["application/json", "text/json"]
|
|
</pre>
|
|
</div>
|
|
<div class="section" id="building-your-documentation-automatically">
|
|
<h2>Building your documentation automatically</h2>
|
|
<p>XXX</p>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="comments">
|
|
<h2>Comments</h2>
|
|
<div id="disqus_thread"></div>
|
|
<script type="text/javascript">
|
|
var disqus_identifier = "introducing-cornice.html";
|
|
(function() {
|
|
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
|
dsq.src = 'http://blog-notmyidea.disqus.com/embed.js';
|
|
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
|
|
})();
|
|
</script>
|
|
</div>
|
|
|
|
</div>
|
|
</body>
|
|
</html> |