"…mais ce serait peut-être l'une des plus grandes opportunités manquées de notre époque si le logiciel libre ne libérait rien d'autre que du code…"

RESTful Resource Oriented Architecture (ROA), HTTP, URI, XML

Posted by patrick sur juillet 11, 2007

Analyse du livre « Restful web services » de Léonard Richardson et Sam Ruby, Copyright 2007, O’Reilly Media, Inc., ISBN-13: 978-0-596-52926-0.

Pour les exemples du livre on a besoin de:

– les sources du livre (http://examples.oreilly.com/9780596529260/)

Les sources du livre

– comme je suis au travail sous Windows, j’utilise l’émulateur Cygwin pour avoir un environnement de type UNIX.
– les langages semi-interprétés python, ruby, php et les langage C# et java

– différentes bibliothèques logicielles suivant les langages:

  • la bibliothèque Ruby/Amazon (Attention: « Before you can use this library, you need to obtain an Amazon Web Services developer token« ).
    • Pour l’installer
      • $ ruby setup.rb config
        —> lib
        —> lib/amazon
        —> lib/amazon/search
        —> lib/amazon/search/exchange
        <— lib/amazon/search/exchange
        <— lib/amazon/search
        <— lib/amazon
        <— lib
      • $ ruby setup.rb setup
        —> lib
        —> lib/amazon
        —> lib/amazon/search
        —> lib/amazon/search/exchange
        <— lib/amazon/search/exchange
        <— lib/amazon/search
        <— lib/amazon
        <— lib
      • $ ruby setup.rb install
        —> lib
        mkdir -p /usr/lib/ruby/site_ruby/1.8/
        install amazon.rb /usr/lib/ruby/site_ruby/1.8/
        —> lib/amazon
        mkdir -p /usr/lib/ruby/site_ruby/1.8/amazon
        install search.rb /usr/lib/ruby/site_ruby/1.8/amazon
        install wishlist.rb /usr/lib/ruby/site_ruby/1.8/amazon
        install weddingregistry.rb /usr/lib/ruby/site_ruby/1.8/amazon
        install babyregistry.rb /usr/lib/ruby/site_ruby/1.8/amazon
        install transaction.rb /usr/lib/ruby/site_ruby/1.8/amazon
        install locale.rb /usr/lib/ruby/site_ruby/1.8/amazon
        install shoppingcart.rb /usr/lib/ruby/site_ruby/1.8/amazon
        —> lib/amazon/search
        mkdir -p /usr/lib/ruby/site_ruby/1.8/amazon/search
        install seller.rb /usr/lib/ruby/site_ruby/1.8/amazon/search
        install blended.rb /usr/lib/ruby/site_ruby/1.8/amazon/search
        install exchange.rb /usr/lib/ruby/site_ruby/1.8/amazon/search
        install cache.rb /usr/lib/ruby/site_ruby/1.8/amazon/search
        —> lib/amazon/search/exchange
        mkdir -p /usr/lib/ruby/site_ruby/1.8/amazon/search/exchange
        install marketplace.rb /usr/lib/ruby/site_ruby/1.8/amazon/search/exchange
        install thirdparty.rb /usr/lib/ruby/site_ruby/1.8/amazon/search/exchange
        <— lib/amazon/search/exchange
        <— lib/amazon/search
        <— lib/amazon
        <— lib
  • la commande s3sh pour Ruby (http://amazon.rubyforge.org/ , AWS::S3 A Ruby Library for Amazon’s Simple Storage Service’s (S3) REST API).
    • pve@pc72 /cygdrive/e/Temp/rubygems-0.9.4/rubygems-0.9.4
      $ gem i aws-s3 -ry
      Bulk updating Gem source index for: http://gems.rubyforge.org
      Successfully installed aws-s3-0.4.0
      Successfully installed xml-simple-1.0.11
      Successfully installed builder-2.1.2
      Successfully installed mime-types-1.15
      Installing ri documentation for aws-s3-0.4.0…
      Installing ri documentation for builder-2.1.2…
      Installing ri documentation for mime-types-1.15…
      Installing RDoc documentation for aws-s3-0.4.0…
      Installing RDoc documentation for builder-2.1.2…
      Installing RDoc documentation for mime-types-1.15…
      pve@pc72 /cygdrive/e/Temp/rubygems-0.9.4/rubygems-0.9.4

Test du premier programme ruby:

#!/usr/bin/ruby -w
# amazon-book-search.rb
require ‘amazon/search’

if ARGV.size != 2
puts « Usage: #{$0} [Amazon Web Services AccessKey ID] [text to search for] »
exit
end
access_key, search_request = ARGV

req = Amazon::Search::Request.new(access_key)
# For every book in the search results…
req.keyword_search(search_request, ‘books’, Amazon::Search::LIGHT) do |book|
# Print the book’s name and the list of authors.
puts %{« #{book.product_name} » by #{book.authors.join(‘, ‘)}}
end

  • $ ruby amazon-book-search.rb 126116STACCESSID « restful web services »
    « RESTful Web Services » by Leonard Richardson, Sam Ruby, David Heinemeier Hansson

Bon j’ai détaillé ce premier exemple car je n’avais encore jamais travaillé avec Ruby. Donc quittons cet aspect très pratique pour entrer dans l’aspect plus théorique du livre.

Les auteurs introduisent la notion de « Web programmable » qui est basée sur d’abord et avant tout sur le protocole HTTP et XML (éventuellement HTML, JSON, du texte et du binaire). « If you don’t use HTTP, you’re not on the web« . Il y a donc tout un paragraphe (« HTTP: Documents in Envelopes ») sur HTTP. On y apprend que à part les méthodes GET et POST que tout le monde connait🙂 il y a les méthodes beaucoup plus méconnues que sont HEAD, PUT et DELETE sans compter OPTIONS, TRACE et CONNECT. La méthode ‘GET’ permet de rechercher une ressource, la commande ‘DELETE’ permet de la détruire tandis que la commande ‘PUT’ permet de la mettre à jour.

On en vient à l’exemple de recherche de photos sur flickr (http://www.flickr.com/services/api/keys/apply. On sait qu’on fait une recherche de ressources grâce à l’emploi du nom de la méthode ‘flickr.photos.search’ qui fait référence à la méthode HTTP GET.

Il y a une comparaison entre les 2 URIS équivalentes suivantes:

http://flickr.com/photos/tags/penguin –> la méthode est ‘GET’ et l’information de portée (scoping information) est ‘les photos taggés penguin’

-http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=xxx&tags=penguin (dans le bouquin ils ont oublié de mettre &api_key=xxx) –> la méthode est ‘recherche une photo’ et l’information de portée est ‘penguin’.

Il n’y a pas de différence technique entre les 2 URIs mais il y a une différence au niveau de l’architecture si on emploie une méthode comme ‘flickr.photos.delete’ qui utilisera la méthode HTTP GET à une place qui ne lui était pas destinée (‘into places it wasn’t meant to go’). On a une architecture REST-RPC hybride (p.16)

A partir de là (« RESTful, Resource-Oriented Architectures(ROA), page 13), les auteurs définissent ce qu’ils entendent par de véritables services web Restful: ce sont des services qui resemblent au web et ils les appellent ‘Orientés ressource’ (resource oriented’).

Dans des architectures RESTful ‘Orientées Ressource’ l’information de portée est dans l’URI et l’architecture est RESTful car la méthode d’information est la méthode HTTP. Si l’information de portée (scoping information) n’est pas dans l’URI alors le service n’est pas ‘Orienté Ressource’.

Les services suivants sont considérés comme étant ‘RESTFul, orientés ressource‘:

tandis que les suivants sont considérés comme REST-RPC hybrid:

  • l’API del.icio.us
  • l’API « REST » de flickr
  • la plupart des applications web

Fin du chapitre 1 (ch1)

Dans le chapitre 2 (« Writing Web Service Clients ») on confirme que l’API del.icio.us n’utilise que la méthode HTTP ‘GET’ quel que soit le service demandé ! Le chapitre 7 « A service implementation » montrera ce qu’est un service RESTful pour le service del.icio.us.

Le but de ce chapitre est d’écrire des clients del.icio.us à l’aide de différents langages et d’utiliser en conséquence différentes bibliothèques permettant de faire des requêtes HTTP. Ces bibliothèques doivent respecter ces caractéristiques:

– le support de HTTPS et du certificat SSL

– implémenter au moins les 5 méthodes HTTP suivantes: GET, HEAD, POST, PUT et DELETE. On rappelle que malheureusement certaines bibliothèques ne supportent que GET et POST et parfois même uniquement GET ! Les méthodes comme OPTIONS , TRACE et la méthode MOVE du protocole Webdav sont un bonus.

– doit permettre au programmeur de manipuler les données envoyées lors d’une requête PUT ou POST

– doit permettre au programmeur de modifier les headers HTTP

– doit donner au programmeur au code retour et aux headers HTTP de la réponse

– doit pouvoir communiquer au travers d’un serveur mandataire HTTP.

– etc.

La bibliothèque standard de Ruby (open-uri) ne supporte que la méthode ‘GET’. Sam Ruby a écrit rest-open-uri

$ gem install rest-open-uri
Successfully installed rest-open-uri-1.0.0
Installing ri documentation for rest-open-uri-1.0.0…
Installing RDoc documentation for rest-open-uri-1.0.0…

Pour Python, il y a urllib2 (semblable à open-uri), httplib et l’excellente bibliothèque de Joe Gregorio httplib2 (http://bitworking.org/projects/httplib2) supportant pratiquement toutes les caractéristiques d’une bonne bibliothèqueHTTP.

pve@pc72 /cygdrive/e/Temp/httplib2-0.3.0
$ python setup.py install
running install
running build
running build_py
creating build
creating build/lib
creating build/lib/httplib2
copying httplib2/__init__.py -> build/lib/httplib2
copying httplib2/iri2uri.py -> build/lib/httplib2
running install_lib
creating /usr/lib/python2.5/site-packages/httplib2
running install_egg_info
Writing /usr/lib/python2.5/site-packages/httplib2-0.3.0-py2.5.egg-info

Je passe sur Java et C#. Pour PHP, il y a libcurl; pour Javascript XMLHttpRequest (voir chapitre 11).

En ligne de commande on a cURL (http://curl.haxx.se) qui est livré en standard sous cygwin. Utiliser l’option -v de cURL permet d’avoir des informations très intéressantes.

Pour info: lynx est aussi un outil intéressant pour les apllications web.
On passe ensuite aux parsers XML

– Ruby: REXML(interfaces DOM et SAX, support XPath). Attention: ne rejette pas le XML mal formé. Autres bibliohèques: libxml2 du projet GNOME, hpricot

$ gem install hpricot
Select which gem to install for your platform (i386-cygwin)
1. hpricot 0.6 (mswin32)
2. hpricot 0.6 (jruby)
3. hpricot 0.6 (ruby)
4. hpricot 0.5 (ruby)
5. hpricot 0.5 (mswin32)
6. Skip this gem
7. Cancel installation
> 3
Building native extensions. This could take a while…
Successfully installed hpricot-0.6
Installing ri documentation for hpricot-0.6…
Installing RDoc documentation for hpricot-0.6…

– Python: ElementTree (support XPath limité), 4Suite (support complet de XPath, http://4suite.org), …etc.

Pour le format JSON, voir le site http://www.json.org.

On finit le chapitre par WADL. Voir le site http://tomayac.de/rest-describe/latest/RestDescribe.html.

Fin du chapitre 2.

Chapitre 3, « What makes Restful services different ? »

Les auteurs rappellent que les APIs del.icio.us et Flickr marchent comme le web lorsqu’il s’agit de rechercher des données mais que ce sont des services de style RPC lorsqu’il s’agit de modifier des données. Les différents services de recherche Yahoo! sont très RESTful mais ils sont si simples qu’il ne peuvent servir d’exemples. Par contre le protocole de publication Atom (APP) et le service de stockage simple d’Amazon (S3) décrit dans ce chapitre sont RESTful et orientés ressources. Amazon fournit des biblothèques d’accès à ce service pour différents langages et outils (comme cURL) (http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=47)

Le service S3 RESTful expose toutes les fonctionnalités du service style RPC mais au lieu de le faire avec des noms de fonction spécialisées il expose des objets standards HTTP appelés ‘ressources‘. Au lieu de répondre à des noms de fonctions comme ‘getObjects’ une ressource répond à une ou plusieurs des 4 méthodes HTTP standards: GET, HEAD, PUT et DELETE.

– Pour avoir la valeur d’un objet on envoie la requête ‘GET’ sur l’URI de cet objet

– pour obtenir uniquement les métadonnées de cet objet on envoie la requête ‘HEAD’ sur la même URI

– pour créer un ‘bucket’, on envoie la requête ‘PUT’ sur l’URI de l’objet contenant le nom du bucket

– pour détruire un ‘bucket’ ou un autre objet, on envoie la requête ‘DELETE’ sur l’URI correspondante.

Les concepteurs de S3 n’ont rien fait d’extraordinaire. Suivant le standard HTTP c’est pour faire ce genre de choses que les méthodes GET, HEAD, PUT et DELETE ont été créées !

Les codes de réponse HTTP pour S3 vont de 200 (« OK »), 404 (« NotFound »). La plus commune est 403 (« Forbidden »). S3 utilise aussi les codes 400 (« Bad request ») indiquant que le serveur ne comprend pas les données envoyées par le client, 409 (« Conflict ») envoyé au client qui essaye de détruire un bucket qui n’est pas vide.

Ensuite on crée une bibliothèque Ruby pour impléménter un client S3. Le but est d’illustrer la théorie derrière REST. Au lieu d’utiliser des noms de méthodes spécifiques, on utilisera des noms qui reflèteront l’architecture RESTful: get, put, delete etc… Les auteurs créent un module nommé S3::Authorized dans le fichier S3lib.rb qu’il faut modifier pour entrer sa clé publique et sa clé privée (« Enter your public key (Amazon calls it an « Access Key ID ») and your private key (Amazon calls it a « Secret Access Key ». This is so you can sign your S3 requests and Amazon will know who to charge »)

Je vérifie avec curl la validité de mes clés.

pve@pc72 ~
$ cat > pubkey.txt <<EOF
> 1261ffffffffff8X19802
> EOF

pve@pc72 ~
$ cat > key.txt <<EOF
> tmaTMgnMiyyyyyyyyyyypMCV8O9JdxQ4
> EOF
$ curl -v –key key.txt –pubkey pubkey.txt https://s3.amazonaws.com/
* About to connect() to s3.amazonaws.com port 443 (#0)
* Trying 72.21.203.129… connected
* Connected to s3.amazonaws.com (72.21.203.129) port 443 (#0)
* successfully set certificate verify locations:
* CAfile: /usr/share/curl/curl-ca-bundle.crt
CApath: none
* SSLv2, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using RC4-MD5
* Server certificate:
* subject: /C=US/ST=Washington/L=Seattle/O=Amazon.com Inc./CN=s3.amazonaw
s.com
* start date: 2007-02-20 00:00:00 GMT
* expire date: 2008-02-20 23:59:59 GMT
* common name: s3.amazonaws.com (matched)
* issuer: /C=US/O=RSA Data Security, Inc./OU=Secure Server Certification
Authority
* SSL certificate verify ok.
> GET / HTTP/1.1
> User-Agent: curl/7.16.3 (i686-pc-cygwin) libcurl/7.16.3 OpenSSL/0.9.8e zlib/1.
2.3 libssh2/0.15-CVS
> Host: s3.amazonaws.com
> Accept: */*
>
< HTTP/1.1 307 Temporary Redirect
< x-amz-id-2: PyFBRRXn6ohqSTY/OdVyE54dsts1bEsUPYluggqw67RWYEhGBrDl3Ru55piRhIgf
< x-amz-request-id: 4546F4FDF40D80AF
< Date: Wed, 11 Jul 2007 10:27:39 GMT
< Location: http://aws.amazon.com/s3
< Content-Length: 0
< Server: AmazonS3
<
* Connection #0 to host s3.amazonaws.com left intact
* Closing connection #0
* SSLv3, TLS alert, Client hello (1):

J’ai un problème avec le client ruby:

pve@pc72 /cygdrive/e/projets/REST/ruby/ch3
$ ruby s3-sample-client.rb buck1 obj1 5
/usr/lib/ruby/1.8/net/http.rb:567: warning: using default DH parameters.
/usr/lib/ruby/gems/1.8/gems/rest-open-uri-1.0.0/lib/rest-open-uri.rb:320
n_http’: 403 Forbidden (OpenURI::HTTPError)
from /usr/lib/ruby/gems/1.8/gems/rest-open-uri-1.0.0/lib/rest-op
b:659:in `buffer_open’
……
b:162:in `open_uri’
from /usr/lib/ruby/gems/1.8/gems/rest-open-uri-1.0.0/lib/rest-op
b:561:in `open’
from /usr/lib/ruby/gems/1.8/gems/rest-open-uri-1.0.0/lib/rest-op
b:35:in `open’
from ./S3lib.rb:276:in `open’
from ./S3lib.rb:45:in `get’
from s3-sample-client.rb:13

Ensuite on a un exemple d’utilisation de Ruby on rails pour la création de services RESTful. On installe donc Ruby on rails avec la commande ‘gem install rails’

$ gem install rails
Need to update 13 gems from http://gems.rubyforge.org
………….
complete
Install required dependency rake? [Yn] Y
Install required dependency activesupport? [Yn] Y
Install required dependency activerecord? [Yn] Y
Install required dependency actionpack? [Yn] Y
Install required dependency actionmailer? [Yn] Y
Install required dependency actionwebservice? [Yn] Y
Successfully installed rails-1.2.3
Successfully installed rake-0.7.3
Successfully installed activesupport-1.4.2
Successfully installed activerecord-1.15.3
Successfully installed actionpack-1.13.3
Successfully installed actionmailer-1.3.3
Successfully installed actionwebservice-1.2.3
Installing ri documentation for rake-0.7.3…
Installing ri documentation for activesupport-1.4.2…
Installing ri documentation for activerecord-1.15.3…
Installing ri documentation for actionpack-1.13.3…
Installing ri documentation for actionmailer-1.3.3…
Installing ri documentation for actionwebservice-1.2.3..
Installing RDoc documentation for rake-0.7.3…
Installing RDoc documentation for activesupport-1.4.2…
Installing RDoc documentation for activerecord-1.15.3…
Installing RDoc documentation for actionpack-1.13.3…
Installing RDoc documentation for actionmailer-1.3.3…
Installing RDoc documentation for actionwebservice-1.2.3

pve@pc72 /cygdrive/e/projets/REST/ruby/ch3

On apprend que:

– dans une application Rails, le modèle et le contrôleur du modèle MVC peuvent se comporter comme un service web RESTful. L’interface d’ActiveResource est analogue à l’interface d’ActiveRecord: avec ActiveRecord les objets sont dans une base de données et sont exposés au moyen de SQL avec les commandes SELECT, INSERT, UPDATE et DELETE. Avec ActiveResource ils sont au sein d’une application Rails, exposés au moyen de HTTP avec les commandes GET, POST, PUT et DELETE.

En conclusion, un clone de S3 en Ruby : http://code.whytheluckystiff.net/parkplace

Chapitre 4: The Resource-Oriented Architecture (ROA)

L’architecture ROA est une façon de transformer un problème en service web RESTful: un ensemble d’URIs, d’HTTP et de XML qui marchent comme le reste du web et que les programmeurs aimeront🙂

Les services Web RESTful peuvent être classés suivant les réponses à 2 questions:

– l’information de portée est stockée dans l’URI : (pourquoi le serveur enverrait cette information plutôt qu’une autre ?) C’est le principe d’adressabilité.

– la méthode d’information est la méthode HTTP (pourquoi le serveur devrait-il envoyer cette donnée au lieu de la détruire ?)

Pour les auteurs, REST n’est pas une architecture (ce qui est en contradiction avec la définition donnée par Wikipedia) mais un ensemble de critères de conception. Je ne vais pas plus loin dans leur définition car elle est en contradiction totale de ce que je lis sur Wikipedia. Les auteurs estiment que REST n’a pas de rapport direct avec HTTP et les URIs.

Quelques définitions:

– à une ressource doit correpondre au moins une URI. L’URI est le nom et l’adresse de la ressource. Je fais remarquer que le U dans URI veut dire Uniform et non Universal et ce depuis décembre 1994 (voir http://tools.ietf.org/html/rfc1738)

– une URI devrait être descriptive et avoir une structure (c’est donc une convention à adopter)

– 2 ressources ne peuvent avoir la même URI

– une ressource peut avoir 1 ou plusieurs URI

– une URI désigne par définition une et une seule ressource

– une application est adressable si elle expose ses données en tant que ressources

– le fait d’avoir une URI pour chaque ressource permet l’utilisation de mandataires HTTP (proxies HTTP) pour la mise en cache des ressources

– une requête HTTP est sans état (statelessness)

– une ressource est une source de représentations multiples

– pour désigner des représentations différentes les auteurs recommandent de leur attribuer une URI distincte. Exemple: ajouter un .es, .en, .fr suivant la langue. Une alternative est la négociation du contenu (content negociation, voir les champs HTTP header: Accept-Language et Accept).

Ici les auteurs abordent la notion d’interface uniforme:

– pour lire une ressource on utilise la requête ‘GET

   The GET method means retrieve whatever information (in the form of an
   entity) is identified by the Request-URI. If the Request-URI refers
   to a data-producing process, it is the produced data which shall be
   returned as the entity in the response and not the source text of the
   process, unless that text happens to be the output of the process.

   The semantics of the GET method change to a "conditional GET" if the
   request message includes an If-Modified-Since, If-Unmodified-Since,
   If-Match, If-None-Match, or If-Range header field. A conditional GET
   method requests that the entity be transferred only under the
   circumstances described by the conditional header field(s). The
   conditional GET method is intended to reduce unnecessary network
   usage by allowing cached entities to be refreshed without requiring
   multiple requests or transferring data already held by the client.

   The semantics of the GET method change to a "partial GET" if the
   request message includes a Range header field. A partial GET requests
   that only part of the entity be transferred, as described in section
   14.35. The partial GET method is intended to reduce unnecessary
   network usage by allowing partially-retrieved entities to be
   completed without transferring data already held by the client.

   The response to a GET request is cacheable if and only if it meets
   the requirements for HTTP caching described in section 13

– pour supprimer une ressource on utilise la requête ‘DELETE’

   The DELETE method requests that the origin server delete the resource
   identified by the Request-URI. This method MAY be overridden by human
   intervention (or other means) on the origin server. The client cannot
   be guaranteed that the operation has been carried out, even if the
   status code returned from the origin server indicates that the action
   has been completed successfully. However, the server SHOULD NOT
   indicate success unless, at the time the response is given, it
   intends to delete the resource or move it to an inaccessible
   location.

   A successful response SHOULD be 200 (OK) if the response includes an
   entity describing the status, 202 (Accepted) if the action has not
   yet been enacted, or 204 (No Content) if the action has been enacted
   but the response does not include an entity.

   If the request passes through a cache and the Request-URI identifies
   one or more currently cached entities, those entries SHOULD be
   treated as stale. Responses to this method are not cacheable.

– pour créer ou modifier une ressource utiliser la requête ‘PUT‘ associée à l’URI

   The PUT method requests that the enclosed entity be stored under the
   supplied Request-URI. If the Request-URI refers to an already
   existing resource, the enclosed entity SHOULD be considered as a
   modified version of the one residing on the origin server. If the
   Request-URI does not point to an existing resource, and that URI is
   capable of being defined as a new resource by the requesting user
   agent, the origin server can create the resource with that URI. If a
   new resource is created, the origin server MUST inform the user agent
   via the 201 (Created) response. If an existing resource is modified,
   either the 200 (OK) or 204 (No Content) response codes SHOULD be sent
   to indicate successful completion of the request. If the resource
   could not be created or modified with the Request-URI, an appropriate
   error response SHOULD be given that reflects the nature of the
   problem. The recipient of the entity MUST NOT ignore any Content-*
   (e.g. Content-Range) headers that it does not understand or implement
   and MUST return a 501 (Not Implemented) response in such cases.

   If the request passes through a cache and the Request-URI identifies
   one or more currently cached entities, those entries SHOULD be
   treated as stale. Responses to this method are not cacheable.

   The fundamental difference between the POST and PUT requests is
   reflected in the different meaning of the Request-URI. The URI in a
   POST request identifies the resource that will handle the enclosed
   entity. That resource might be a data-accepting process, a gateway to
   some other protocol, or a separate entity that accepts annotations.
   In contrast, the URI in a PUT request identifies the entity enclosed
   with the request -- the user agent knows what URI is intended and the
   server MUST NOT attempt to apply the request to some other resource.
   If the server desires that the request be applied to a different URI,

Fielding, et al.            Standards Track                    [Page 55]
 
RFC 2616                        HTTP/1.1                       June 1999

   it MUST send a 301 (Moved Permanently) response; the user agent MAY
   then make its own decision regarding whether or not to redirect the
   request.

   A single resource MAY be identified by many different URIs. For
   example, an article might have a URI for identifying "the current
   version" which is separate from the URI identifying each particular
   version. In this case, a PUT request on a general URI might result in
   several other URIs being defined by the origin server.

   HTTP/1.1 does not define how a PUT method affects the state of an
   origin server.

– pour retrouver les métadata d’une ressource utiliser la requête ‘HEAD‘. Un client peut utiliser cette requête pour voir si une requête existe.

   The HEAD method is identical to GET except that the server MUST NOT
   return a message-body in the response. The metainformation contained
   in the HTTP headers in response to a HEAD request SHOULD be identical
   to the information sent in response to a GET request. This method can
   be used for obtaining metainformation about the entity implied by the
   request without transferring the entity-body itself. This method is
   often used for testing hypertext links for validity, accessibility,
   and recent modification.

   The response to a HEAD request MAY be cacheable in the sense that the
   information contained in the response MAY be used to update a
   previously cached entity from that resource. If the new field values
   indicate that the cached entity differs from the current entity (as
   would be indicated by a change in Content-Length, Content-MD5, ETag
   or Last-Modified), then the cache MUST treat the cache entry as
   stale.

– pour savoir quelles opérations sont permises sur une ressource employer la requête ‘OPTIONS‘. Bon c’est de la théorie puisque très peu de serveurs offrent ce support.

   The OPTIONS method represents a request for information about the
   communication options available on the request/response chain
   identified by the Request-URI. This method allows the client to
   determine the options and/or requirements associated with a resource,
   or the capabilities of a server, without implying a resource action
   or initiating a resource retrieval.

   Responses to this method are not cacheable.

   If the OPTIONS request includes an entity-body (as indicated by the
   presence of Content-Length or Transfer-Encoding), then the media type
   MUST be indicated by a Content-Type field. Although this
   specification does not define any use for such a body, future
   extensions to HTTP might use the OPTIONS body to make more detailed
   queries on the server. A server that does not support such an
   extension MAY discard the request body.

   If the Request-URI is an asterisk ("*"), the OPTIONS request is
   intended to apply to the server in general rather than to a specific
   resource. Since a server's communication options typically depend on
   the resource, the "*" request is only useful as a "ping" or "no-op"
   type of method; it does nothing beyond allowing the client to test
   the capabilities of the server. For example, this can be used to test
   a proxy for HTTP/1.1 compliance (or lack thereof).

   If the Request-URI is not an asterisk, the OPTIONS request applies
   only to the options that are available when communicating with that
   resource.

   A 200 response SHOULD include any header fields that indicate
   optional features implemented by the server and applicable to that
   resource (e.g., Allow), possibly including extensions not defined by
   this specification. The response body, if any, SHOULD also include
   information about the communication options. The format for such a

Fielding, et al.            Standards Track                    [Page 52]
 
RFC 2616                        HTTP/1.1                       June 1999

   body is not defined by this specification, but might be defined by
   future extensions to HTTP. Content negotiation MAY be used to select
   the appropriate response format. If no response body is included, the
   response MUST include a Content-Length field with a field-value of
   "0".

   The Max-Forwards request-header field MAY be used to target a
   specific proxy in the request chain. When a proxy receives an OPTIONS
   request on an absoluteURI for which request forwarding is permitted,
   the proxy MUST check for a Max-Forwards field. If the Max-Forwards
   field-value is zero ("0"), the proxy MUST NOT forward the message;
   instead, the proxy SHOULD respond with its own communication options.
   If the Max-Forwards field-value is an integer greater than zero, the
   proxy MUST decrement the field-value when it forwards the request. If
   no Max-Forwards field is present in the request, then the forwarded
   request MUST NOT include a Max-Forwards field.

– la requête HTTP ‘POST‘ est la plus mal comprise des méthodes HTTP:

   The POST method is used to request that the origin server accept the
   entity enclosed in the request as a new subordinate of the resource
   identified by the Request-URI in the Request-Line. POST is designed
   to allow a uniform method to cover the following functions:

      - Annotation of existing resources;

      - Posting a message to a bulletin board, newsgroup, mailing list,
        or similar group of articles;

      - Providing a block of data, such as the result of submitting a
        form, to a data-handling process;

      - Extending a database through an append operation.

   The actual function performed by the POST method is determined by the
   server and is usually dependent on the Request-URI. The posted entity
   is subordinate to that URI in the same way that a file is subordinate
   to a directory containing it, a news article is subordinate to a
   newsgroup to which it is posted, or a record is subordinate to a
   database.

   The action performed by the POST method might not result in a
   resource that can be identified by a URI. In this case, either 200
   (OK) or 204 (No Content) is the appropriate response status,
   depending on whether or not the response includes an entity that
   describes the result.

Fielding, et al.            Standards Track                    [Page 54]
 
RFC 2616                        HTTP/1.1                       June 1999

   If a resource has been created on the origin server, the response
   SHOULD be 201 (Created) and contain an entity which describes the
   status of the request and refers to the new resource, and a Location
   header (see section 14.30).

   Responses to this method are not cacheable, unless the response
   includes appropriate Cache-Control or Expires header fields. However,
   the 303 (See Other) response can be used to direct the user agent to
   retrieve a cacheable resource.

Dans une conception RESTful, la méthode ‘POST’ signifie ‘ajouter’ (append) une ressource à une ressource existante. La différence entre ‘PUT’ et ‘POST’ est la suivante: le client utilise la méthode ‘PUT’ lorsque c’est le client qui décide de l’URI de la ressource; le client utilise la méthode ‘POST’ lorsque c’est le serveur qui décide de l’URI qui identifiera la nouvelle ressource. Autrement dit la méthode ‘POST est une façon de créer une nouvelle ressource sans que le client ait la connaissance exacte de sa future URI. Dans la plupart des cas, le client connait l’URI du parent de la ressource. La réponse à ce type de POST est généralement 201 (‘Created’). Une fois que cette ressource exist, l’URI est définie et le client peut utiliser les méthodes ‘PUT’, ‘GET’ et ‘DELETE’.

Mais la signification que 99,9% des développeurs connaissent est le ‘POST’ surchargé: c’est la fameuse requête ‘POST’ employée dans les formulaires HTML: l’information sur ce qui doit être fait est contenue dans la requête HTTP (URI, entêtes HTTP, ou le corps de la requête). C’est d’après les auteurs une chose inévitable…!

– GET et HEAD sont des méthodes sûres (safe).

   Implementors should be aware that the software represents the user in
   their interactions over the Internet, and should be careful to allow
   the user to be aware of any actions they might take which may have an
   unexpected significance to themselves or others.

   In particular, the convention has been established that the GET and
   HEAD methods SHOULD NOT have the significance of taking an action
   other than retrieval. These methods ought to be considered "safe".
   This allows user agents to represent other methods, such as POST, PUT
   and DELETE, in a special way, so that the user is made aware of the
   fact that a possibly unsafe action is being requested.

   Naturally, it is not possible to ensure that the server does not
   generate side-effects as a result of performing a GET request; in
   fact, some dynamic resources consider that a feature. The important
   distinction here is that the user did not request the side-effects,
   so therefore cannot be held accountable for them.

– GET, HEAD, PUT et DELETE sont idempotentes.

   Methods can also have the property of "idempotence" in that (aside
   from error or expiration issues) the side-effects of N > 0 identical
   requests is the same as for a single request. The methods GET, HEAD,
   PUT and DELETE share this property. Also, the methods OPTIONS and
   TRACE SHOULD NOT have side effects, and so are inherently idempotent.

Fielding, et al.            Standards Track                    [Page 51]
RFC 2616                        HTTP/1.1                       June 1999

   However, it is possible that a sequence of several requests is non-
   idempotent, even if all of the methods executed in that sequence are
   idempotent. (A sequence is idempotent if a single execution of the
   entire sequence always yields a result that is not changed by a
   reexecution of all, or part, of that sequence.) For example, a
   sequence is non-idempotent if its result depends on a value that is
   later modified in the same sequence.

   A sequence that never has side effects is idempotent, by definition
   (provided that no concurrent operations are being executed on the
   same set of resources).

– la méthode ‘POST’ n’est ni sûre (safe) ni idempotente.

– les auteurs signalent que la méthode ‘GET’ n’a pas été utilisée correctement sur le Web, cette méthode pouvant être unsafe ! Voir Google et son WebAccelerator de 2005.

– les auteurs finissent en constatant que actuellement les formulaires HTML n’autorisent que POST et GET (p.153 les auteurs utilisent ‘PUT’ et ‘DELETE’ qui font partie de la future norme XHTM5, voir http://cafe.elharo.com/web/why-rest-failed/ )

http://www.w3.org/TR/html401/interact/forms.html#adef-method

method = get|post [CI]
This attribute specifies which HTTP method will be used to submit the form data set. Possible (case-insensitive) values are « get » (the default) and « post ». See the section on form submission for usage information.

http://www.whatwg.org/specs/web-forms/current-work/#methodAndEnctypes

« The HTTP specification defines various methods that can be used with HTTP URIs. Four of these may be used as values of the method attribute: get, post, put, and delete. In this specification, these method names are applied to other protocols as well. This section defines how they should be interpreted.

If the specified method is not one of get, post, put, or delete then it is treated as get in the tables below. »

Chapitre 5: « Designing Read_only Resource-Oriented Services

D’abord on définit son ensemble de données (Data Set) et on décide de découper ses données en ressources. Au lieu de penser en termes d’actions, il faut penser en termes de résultats d’une action.

Ensuite, il faut nommer ces ressources (« Name the Resources »). Il faut se rappeler que dans un service orienté ressources, l’URI contient toutes les informations de portée. Quelques conseils:

– utiliser les chemins pour encoder une hiérarchie: /parent/enfant

– utiliser des caractères de ponctuation pour éviter d’employer à nouveau une hiérarchie: /parent/enfant1;enfant2

– utiliser des variables de recherche pour indiquer que l’on attend des entrées: /search?q=cyliste&name=poupou

Maintenant, il faut décider quelles données renvoyées au client et quels formats employer pour ces données (choix de la représentation) lorsqu’un client demande une ressource. Le principal but d’une représentation est de transmettre l’état de la ressource. Une représentation peut être au format XML (avec un vocabulaire spécifique comme XHTML, ATOM, …etc)

En ce qui concerne les réponses HTTP on aura:

– 200 (« OK)

– le Content-Type de l’entête HTTP sera: application/xhtml+xml pour les résultats de recherche et image/png pour les images.

– les auteurs parlent de la méthode ‘GET’ conditionnelle. Il faut utiliser les champs ‘Last-Modified‘, ‘If-Modified-Since‘, la réponse 304 (« Not Modified ») permettant de ne pas recharger une donnée qui n’a pas changée. Voir Chapitre 8 pour plus de détails.



14.25 If-Modified-Since

   The If-Modified-Since request-header field is used with a method to
   make it conditional: if the requested variant has not been modified
   since the time specified in this field, an entity will not be
   returned from the server; instead, a 304 (not modified) response will
   be returned without any message-body.

       If-Modified-Since = "If-Modified-Since" ":" HTTP-date

Fielding, et al.            Standards Track                   [Page 130]
 
RFC 2616                        HTTP/1.1                       June 1999

   An example of the field is:

       If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT

   A GET method with an If-Modified-Since header and no Range header
   requests that the identified entity be transferred only if it has
   been modified since the date given by the If-Modified-Since header.
   The algorithm for determining this includes the following cases:

      a) If the request would normally result in anything other than a
         200 (OK) status, or if the passed If-Modified-Since date is
         invalid, the response is exactly the same as for a normal GET.
         A date which is later than the server's current time is
         invalid.

      b) If the variant has been modified since the If-Modified-Since
         date, the response is exactly the same as for a normal GET.

      c) If the variant has not been modified since a valid If-
         Modified-Since date, the server SHOULD return a 304 (Not
         Modified) response.

   The purpose of this feature is to allow efficient updates of cached
   information with a minimum amount of transaction overhead.

      Note: The Range request-header field modifies the meaning of If-
      Modified-Since; see section 14.35 for full details.

      Note: If-Modified-Since times are interpreted by the server, whose
      clock might not be synchronized with the client.

      Note: When handling an If-Modified-Since header field, some
      servers will use an exact date comparison function, rather than a
      less-than function, for deciding whether to send a 304 (Not
      Modified) response. To get best results when sending an If-
      Modified-Since header field for cache validation, clients are
      advised to use the exact date string received in a previous Last-
      Modified header field whenever possible.

      Note: If a client uses an arbitrary date in the If-Modified-Since
      header instead of a date taken from the Last-Modified header for
      the same request, the client should be aware of the fact that this
      date is interpreted in the server's understanding of time. The
      client should consider unsynchronized clocks and rounding problems
      due to the different encodings of time between the client and
      server. This includes the possibility of race conditions if the
      document has changed between the time it was first requested and
      the If-Modified-Since date of a subsequent request, and the

Fielding, et al.            Standards Track                   [Page 131]
 
RFC 2616                        HTTP/1.1                       June 1999

      possibility of clock-skew-related problems if the If-Modified-
      Since date is derived from the client's clock without correction
      to the server's clock. Corrections for different time bases
      between client and server are at best approximate due to network
      latency.

   The result of a request having both an If-Modified-Since header field
   and either an If-Match or an If-Unmodified-Since header fielfields is
   undefined by this specification

10.3.5 304 Not Modified

If the client has performed a conditional GET request and access is
allowed, but the document has not been modified, the server SHOULD
respond with this status code. The 304 response MUST NOT contain a
message-body, and thus is always terminated by the first empty line
after the header fields.

The response MUST include the following header fields:

- Date, unless its omission is required by section 14.18.1

Fielding, et al. Standards Track [Page 63]

RFC 2616 HTTP/1.1 June 1999

If a clockless origin server obeys these rules, and proxies and
clients add their own Date to any response received without one (as
already specified by [RFC 2068], section 14.19), caches will operate
correctly.

- ETag and/or Content-Location, if the header would have been sent
in a 200 response to the same request

- Expires, Cache-Control, and/or Vary, if the field-value might
differ from that sent in any previous response for the same
variant

If the conditional GET used a strong cache validator (see section
13.3.3), the response SHOULD NOT include other entity-headers.
Otherwise (i.e., the conditional GET used a weak validator), the
response MUST NOT include other entity-headers; this prevents
inconsistencies between cached entity-bodies and updated headers.

If a 304 response indicates an entity not currently cached, then the
cache MUST disregard the response and repeat the request without the
conditional.

If a cache uses a received 304 response to update a cache entry, the
cache MUST update the entry to reflect any new field values given in
the response.

Chapitre 6: « Designing Read/Write Resource-Oriented Services »

Qui dit écriture dit ouverture d’un compte utilisateur qui devient une ressource avec les problèmes d’authentification et d’autorisation associés. Il faut utiliser le champ entête ‘Authorization


14.8 Authorization

A user agent that wishes to authenticate itself with a server-- usually, but not necessarily, after receiving a 401 response--does so by including an Authorization request-header field with the request. The Authorization field value consists of credentials containing the authentication information of the user agent for the realm of the resource being requested. Authorization = "Authorization" ":" credentials HTTP access authentication is described in "HTTP Authentication: Basic and Digest Access Authentication" [43]. If a request is authenticated and a realm specified, the same credentials SHOULD be valid for all other requests within this realm (assuming that the authentication scheme itself does not require otherwise, such as credentials that vary according to a challenge value or using synchronized clocks). When a shared cache (see section 13.7) receives a request containing an Authorization field, it MUST NOT return the corresponding response as a reply to any other request, unless one of the following specific exceptions holds: 1. If the response includes the "s-maxage" cache-control directive, the cache MAY use that response in replying to a subsequent request. But (if the specified maximum age has passed) a proxy cache MUST first revalidate it with the origin server, using the request-headers from the new request to allow the origin server to authenticate the new request. (This is the defined behavior for s-maxage.) If the response includes "s- maxage=0", the proxy MUST always revalidate it before re-using it. 2. If the response includes the "must-revalidate" cache-control directive, the cache MAY use that response in replying to a subsequent request. But if the response is stale, all caches MUST first revalidate it with the origin server, using the request-headers from the new request to allow the origin server to authenticate the new request. 3. If the response includes the "public" cache-control directive, it MAY be returned in reply to any subsequent request.

Si la réponse est 401 (« Unauthorized ») la réponse doit inclure champ entête ‘WWW-Authenticate‘.


10.4.2 401 Unauthorized

The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8). If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity might include relevant diagnostic information. HTTP access authentication is explained in "HTTP Authentication: Basic and Digest Access Authentication" [43].

Les principaux mécanismes d’authentification sont ‘HTTP basic, ‘HTTP Digest’ (http://tools.ietf.org/html/rfc2617, HTTP Authentication: Basic and Digest Access Authentication) et ‘WSSE’ qui sont très peu sécurisés. Voir chapitre 8 pour plus de détails.

Les auteurs parlent à nouveau de XHTML5 (http://www.whatwg.org/specs/web-apps/current-work/, HTML 5 Working Draft — 28 June 2007) qui permettra (quand ?) d’utiliser ‘PUT’ et ‘DELETE’ dans les formulaires. Ils parlent du nouvel attribut ‘template’ (pas encore approuvé , voir http://tools.ietf.org/id/draft-gregorio-uritemplate-00.txt )

   A URI Template is a sequence of characters that contains one or more
   embedded template variables Section 4.1.  A URI Template becomes a
   URI when the template variables are substituted with the template
   variables string values, see Section 4.2.  The following shows an
   example URI Template:

   http://example.com/widgets/{widget_id}

   If the value of the widget_id variable is "xyzzy", the resulting URI
   after substitution is:

   http://example.com/widgets/xyzzy

Chapitre 7: « A service implementation »

Les auteurs rappellent que jusqu’à présent les frameworks Web se sont focalisés sur les applications web pour les butineurs Web (web browser) et qu’ils n’utilisaient donc que les 2 seuls méthodes possibles GET et ‘POST’ (voir http://cafe.elharo.com/web/why-rest-failed/ et http://www.w3.org/TR/html401/interact/forms.html#adef-method).

De nouveaux frameworks pour développer des services RESTful sont apparus: Django (Python), Restlet (Java) et Ruby On Rails. (ces frameworks seront détaillés au chapitre 12)

Le but de ce chapitre est d’écrire une application de type ‘social bookmarking’ RESTful avec RubyOnRails. Alors allons-y !

pve@pc72 /cygdrive/e/projets
$ rails bookmarks
create
create app/controllers
create app/helpers
create app/models
create app/views/layouts
create config/environments
create components
create db
create doc
create lib
create lib/tasks
create log
create public/images
create public/javascripts
create public/stylesheets
create script/performance
create script/process
create test/fixtures
create test/functional
create test/integration
create test/mocks/developmen
create test/mocks/test
create test/unit
create vendor
create vendor/plugins
create tmp/sessions
create tmp/sockets
create tmp/cache
create tmp/pids
create Rakefile
create README
create app/controllers/appli
create app/helpers/applicati
create test/test_helper.rb
create config/database.yml
create config/routes.rb
create public/.htaccess
create config/boot.rb
create config/environment.rb
create config/environments/p
create config/environments/d
create config/environments/t
create script/about
create script/breakpointer
create script/console
create script/destroy
create script/generate
create script/performance/be
create script/performance/pr
create script/process/reaper
create script/process/spawne
create script/process/inspec
create script/runner
create script/server
create script/plugin
create public/dispatch.rb
create public/dispatch.cgi
create public/dispatch.fcgi
create public/404.html
create public/500.html
create public/index.html
create public/favicon.ico
create public/robots.txt
create public/images/rails.p
create public/javascripts/pr
create public/javascripts/ef
create public/javascripts/dr
create public/javascripts/co
create public/javascripts/ap
create doc/README_FOR_APP
create log/server.log
create log/production.log
create log/development.log
create log/test.log

pve@pc72 /cygdrive/e/projets

$ script/plugin discover
Add http://www.agilewebdevelopment.com/plugins/? [Y/n] Y
Add svn://rubyforge.org/var/svn/expressica/plugins/? [Y/n] Y
Add http://soen.ca/svn/projects/rails/plugins/? [Y/n] Y
Add http://technoweenie.stikipad.com/plugins/? [Y/n] Y
Add http://svn.techno-weenie.net/projects/plugins/? [Y/n] Y
Add http://svn.recentrambles.com/plugins/? [Y/n] Y
Add http://opensvn.csie.org/rails_file_column/plugins/? [Y/n] Y
Add http://svn.protocool.com/rails/plugins/? [Y/n] Y
Add http://tools.assembla.com/svn/breakout/breakout/vendor/plugins/? [Y/
Add http://svn.pragprog.com/Public/plugins/? [Y/n] Y
Add http://source.collectiveidea.com/public/rails/plugins/? [Y/n] Y
Add https://secure.near-time.com/svn/plugins/? [Y/n] Y
Add http://svn.inlet-media.de/svn/rails_extensions/plugins/? [Y/n] Y
Add http://svn.viney.net.nz/things/rails/plugins/? [Y/n] Y
Add http://svn.hasmanythrough.com/public/plugins/? [Y/n] Y
Add http://svn.shiftnetwork.com/plugins/? [Y/n] Y
Add svn://caboo.se/plugins/? [Y/n] Y
Add http://svn.6brand.com/projects/plugins/? [Y/n] Y
Add http://shanesbrain.net/svn/rails/plugins/? [Y/n] Y
Add svn://errtheblog.com/svn/plugins/? [Y/n] Y
Add http://svn.nkryptic.com/plugins/? [Y/n] Y
Add http://svn.thoughtbot.com/plugins/? [Y/n] Y
Add http://svn.webwideconsulting.com/plugins/? [Y/n] Y
Add http://invisible.ch/svn/projects/plugins/? [Y/n] Y
Add svn://rubyforge.org/var/svn/enum-column/plugins/? [Y/n] Y
Add http://streamlinedframework.org:8079/streamlined/plugins/? [Y/n] Y
Add svn://dvisionfactory.com/rails/plugins/? [Y/n] Y
Add http://hivelogic.com/plugins/? [Y/n] Y
Add http://mattmccray.com/svn/rails/plugins/? [Y/n] Y
Add svn://rubyforge.org/var/svn/cartographer/plugins/? [Y/n] Y
Add http://www.svn.recentrambles.com/plugins/? [Y/n] Y
Add http://tanjero.com/svn/plugins/? [Y/n] Y
Add http://filetofsole.org/svn/public/projects/rails/plugins/? [Y/n] Y
Add http://topfunky.net/svn/plugins/? [Y/n] Y
Add http://svn.joshpeek.com/projects/plugins/? [Y/n] Y
Add svn://rubyforge.org/var/svn/agtools/plugins/? [Y/n] Y
Add http://svn.aviditybytes.com/rails/plugins/? [Y/n] Y
Add http://beautifulpixel.textdriven.com/svn/plugins/? [Y/n] Y
Add http://mabs29.googlecode.com/svn/trunk/plugins/? [Y/n] Y
Add http://www.codyfauser.com/svn/projects/plugins/? [Y/n] Y
Add http://craz8.com/svn/trunk/plugins/? [Y/n] Y
Add http://sean.treadway.info/svn/plugins/? [Y/n] Y
Add http://svn.thebootstrapnation.com/public/plugins/? [Y/n] Y
Add http://www.mattmccray.com/svn/rails/plugins/? [Y/n] Y
Add svn://rubyforge.org//var/svn/validaterequest/plugins/? [Y/n] Y
Add http://sprocket.slackworks.com/svn/rails/plugins/? [Y/n] Y
Add http://svn.simpltry.com/plugins/? [Y/n] Y
Add http://svn.elctech.com/svn/public/plugins/? [Y/n] Y
Add http://xmlblog.stikipad.com/plugins/? [Y/n] Y
Add http://www.xml-blog.com/svn/plugins/? [Y/n] Y
Add http://svn.toolbocks.com/plugins/? [Y/n] Y
Add http://thar.be/svn/projects/plugins/? [Y/n] Y
Add http://code.teytek.com/rails/plugins/? [Y/n] Y
Add http://www.infused.org/svn/plugins/? [Y/n] Y
Add svn://rubyforge.org/var/svn/apptrain/trunk/vendor/plugins/? [Y/n] Y
Add http://s3cachestore.googlecode.com/svn/trunk/plugins/? [Y/n] Y
Add http://sbecker.net/shared/plugins/? [Y/n] Y
Add http://opensvn.csie.org/macaque/plugins/? [Y/n] Y
Add http://svn.designbyfront.com/rails/plugins/? [Y/n] Y
Add http://svn.rails-engines.org/plugins/? [Y/n] Y
Add http://dev.fiatdev.com/svn/plugins/? [Y/n] Y
Add http://john.guen.in/svn/plugins/? [Y/n] Y
Add http://www.redhillonrails.org/svn/trunk/vendor/plugins/? [Y/n] Y
Add svn://rubyforge.org/var/svn/actsdisjoint/plugins/? [Y/n] Y
Add http://ajaxmessaging.googlecode.com/svn/trunk/plugins/? [Y/n] Y
Add http://mod-i18n.googlecode.com/svn/trunk/plugins/? [Y/n] Y

$ script/plugin install acts_as_taggable
svn: Can’t connect to host ‘rubyforge.org’: Connect
svn: Can’t connect to host ‘caboo.se’: Connection t
svn: Can’t connect to host ‘errtheblog.com’: Connec
svn: Can’t connect to host ‘rubyforge.org’: Connect
svn: Can’t connect to host ‘dvisionfactory.com’: Co
svn: Can’t connect to host ‘rubyforge.org’: Connect
+ ./acts_as_taggable/MIT-LICENSE
+ ./acts_as_taggable/README
+ ./acts_as_taggable/generators/acts_as_taggable_ta
generator.rb
+ ./acts_as_taggable/generators/acts_as_taggable_ta
+ ./acts_as_taggable/init.rb
+ ./acts_as_taggable/lib/acts_as_taggable.rb
+ ./acts_as_taggable/lib/tag.rb
+ ./acts_as_taggable/lib/tagging.rb
+ ./acts_as_taggable/tasks/acts_as_taggable_tasks.r
+ ./acts_as_taggable/test/acts_as_taggable_test.rb

$ script/plugin install http_authentication
svn: Can’t connect to host ‘rubyforge.org’:
svn: Can’t connect to host ‘caboo.se’: Conn
svn: Can’t connect to host ‘errtheblog.com’
svn: Can’t connect to host ‘rubyforge.org’:
svn: Can’t connect to host ‘dvisionfactory.
svn: Can’t connect to host ‘rubyforge.org’:
svn: Can’t connect to host ‘rubyforge.org’:
svn: Can’t connect to host ‘rubyforge.org’:
svn: Can’t connect to host ‘rubyforge.org’:
svn: Can’t connect to host ‘rubyforge.org’:
Plugin not found: [« http_authentication »]

Chapitre 8: « REST and ROA Best Practices »

Une récapitulation des bonnes pratiques.

Chapitre 9: « The building Blocks of Services »

Pour rappeler que les services Web sont basés sur 3 technologies fondamentales: HTTP, URIs et XML.

Les auteurs nous parlent des formats de représentation tels que XHTML (mime-type: application/xhtml+xml), les microformats, Atom, OpenSearch, SVG, form-encode Key-Value Pairs (application/x-www-form-urlencode), JSON, RDF, RDFa.

On nous parle de l’encodage de caractères . Aux Etats Unis, on utilise UTF-8, US-ASCII ou Windows 1252. En Europe on utilise l’ISO-8859-1. Au Japon on utilise EUC-JP, Shift-JS ou UTF-8.

L’utilisation de la norme Unicode permet de mettre de l’ordre dans l’utilisation de tous ces différents codages en utilisant 2 formes de transformation universelle que sont UTF-8 et UTF-16 (pour les langues asiatiques).

Il existe un excellent détecteur d’encode universal écrit en python (http://chardet.feedparser.org)

 

>>> import urllib
>>> urlread = lambda url: urllib.urlopen(url).read()
>>> import chardet
>>> chardet.detect(urlread("http://google.cn/"))
{'encoding': 'GB2312', 'confidence': 0.99}

>>> chardet.detect(urlread("http://yahoo.co.jp/"))
{'encoding': 'EUC-JP', 'confidence': 0.99}

>>> chardet.detect(urlread("http://amazon.co.jp/"))
{'encoding': 'SHIFT_JIS', 'confidence': 1}

>>> chardet.detect(urlread("http://pravda.ru/"))
{'encoding': 'windows-1251', 'confidence': 0.9355}

>>> chardet.detect(urlread("http://auction.co.kr/"))
{'encoding': 'EUC-KR', 'confidence': 0.99}

>>> chardet.detect(urlread("http://haaretz.co.il/"))
{'encoding': 'windows-1255', 'confidence': 0.99}

>>> chardet.detect(urlread("http://www.nectec.or.th/tindex.html"))
{'encoding': 'TIS-620', 'confidence': 0.7675}

>>> chardet.detect(urlread("http://feedparser.org/docs/"))
{'encoding': 'utf-8', 'confidence': 0.99}


Copyright © 2006 Mark Pilgrim · mark@diveintomark.org · Terms of use

XML permet de dire au client quel encodage on a utilisé en le spécifiant sur la première ligne du fichier XML:

<?xml version= »1.0″ encoding= »UTF-8″?>

HTTP avec son attribut d’entête Content-Type peut indiquer également l’encodage utilisé et il est préférable que cela soit le même que le document XML transmis. Si il est différent c’est le codage défini pat Content-Type qui prime. Ceci est un piège !


14.17 Content-Type

The Content-Type entity-header field indicates the media type of the entity-body sent to the recipient or, in the case of the HEAD method, the media type that would have been sent had the request been a GET. Content-Type = "Content-Type" ":" media-type Media types are defined in section 3.7. An example of the field is Content-Type: text/html; charset=ISO-8859-4 Further discussion of methods for identifying the media type of an entity is provided in section 7.2.1.

Pour ces problèmes voir http://tools.ietf.org/html/rfc3023 (XML Media Types) et l’article de Mark Pilgrim « XML on the Web Has Failed » http://www.xml.com/pub/a/2004/07/21/dive.html

Description des protocoles:

APP , http://tools.ietf.org/wg/atompub/ .

– GData qui est une extension de APP (http://code.google.com/apis/gdata/clientlibs.html). Les applications Blogger, Google Calendar, Google code search et Google spreadsheet exposent toutes des services web RESTful avec la même interface : le Protocole de Publication Atom (APP) avec les extensions GData.

Les auteurs parlent ensuite de ‘POE’ (Post Once Exactly, http://www.mnot.net/drafts/draft-nottingham-http-poe-00.txt ), des URI templates, de XHTML4 et de ses nombreuses limitations.

Pour XHTML5 le problème est sa date d’adoption: de fin 2008 à 2022 pour les plus réalistes😦. Voir http://blog.welldesignedurls.org)

Je passe sur WADL.

Chapitre 10 : The Resource-Oriented Architecture Versus Big Web Services

Bon ce sont toutes les technologies SOAP, WSDL WS-: je passe.

Chapitre 11: Ajax applications as REST clients

Pour les auteurs une application AJAX est un service web client qui tourne à l’intérieur d’un butineur (web browser)

Gmail est un service web et il existe une bibliothèque d’accès à ce service: http://libgmail.sourceforge.net (http://libgmail.sourceforge.net/, « Python binding for Google’s Gmail service »).

AJAX est devenu Ajax car pour les auteurs Ajax est un style d’architecture qui n’a pas nécessairement besoin ni de Javascript(on peut utiliserActionScript, Java, VBScript, python) ni de XML (on peut utiliser JSON, du HTML, du texte).

Presque tous les butineurs fournissent un objet javascript XMLHttpRequest avec les 5 méthodes HTTP de base: GET, HEAD, POST, PUT et DELETE avec la possibilité de modifier l’entête et le corps d’une requête HTTP. Un site pour tester son navigateur: http://mnot.net/javascript/xmlhttprequest/

Etant donné les différences d’implémentation de Javascript il est fortement conseillé d’employer des bibliothèques.

Bibliothèques utilisées pour Javascript:

Prototype : Attention: on ne peut pas modifier les champs d’entête !

Dojo

Pour plus d’informations sur les biblothèques Javascript voir http://en.wikipedia.org/wiki/Category:JavaScript_libraries . La bibliothèque Javascript qui monte est la bibliothèque jQuery voir http://docs.jquery.com/Sites_Using_jQuery

Je passe sur JoD (Javascript on Demand)

Chapitre 12 : Frameworks for RESTful Services

Dans ce dernier chapitre, les auteurs passent en revue 3 frameworks REStful: Ruby On rails, Restlet (Java) et Django (Python).

Ruby On Rails:

doit son succès au fait de respecter des conventions. La version 1.2 a une conception RESTful

Restlet

Restlet a été influencé par les technologies majeures de Java: l’API Servlet, les Java Server Pages , HTTPUrlConnection et Struts .

Voir Retrotranslator (http://retrotranslator.sourceforge.net/#what)

Django

La conception de Django est similaire à celle de Rails bien qu’ils fassent moins de simplifications.

Installation:

# extraction des sources django
$ svn co http://code.djangoproject.com/svn/django/trunk/ ~/django_src

$ python -c « from distutils.sysconfig import get_python_lib; print get_python_lib() »
/usr/lib/python2.5/site-packages

$ ln -s /cygdrive/h/django_src/django /usr/lib/python2.5/site-packages/django

# on met django-admin.py dans le chemin PATH

$ ln -s ~/django_src/django/bin/django-admin.py /usr/local/bin

pve@pc72 ~/django_src
$ which django-admin.py
/usr/local/bin/django-admin.py

// Le lendemain ==> mise à jour de django

$ cd ~/django_src; svn update
U django/test/client.py
U django/contrib/auth/__init__.py
U tests/modeltests/test_client/fixtures/testdata.json
U tests/modeltests/test_client/models.py
Updated to revision 5678.

# installation du projet django: voir http://www.djangoproject.com/documentation/tutorial01/

cd /cygdrive/e/projets/

Writing your first Django app, part 1 (http://www.djangoproject.com/documentation/tutorial01/)

Création du projet pybookmarks

$ date; django-admin.py startproject pybookmarks; date
Fri Jul 13 09:03:02 2007
Fri Jul 13 09:03:04 2007

pve@pc72 /cygdrive/e/projets
$ ls -als pybookmarks/
total 48
0 drwxr-xr-x 2 pve mkgroup-l-d 0 Jul 13 09:03 .
0 drwxr-xr-x 6 pve mkgroup-l-d 0 May 10 11:19 ..
0 -rw-r–r– 1 pve mkgroup-l-d 0 Jul 13 09:03 __init__.py
16 -rwxr-xr-x 1 pve mkgroup-l-d 546 Jul 13 09:03 manage.py
16 -rw-r–r– 1 pve mkgroup-l-d 2933 Jul 13 09:03 settings.py
16 -rw-r–r– 1 pve mkgroup-l-d 235 Jul 13 09:03 urls.py

pve@pc72 /cygdrive/e/projets/pybookmarks
$ python manage.py runserver
Validating models…
0 errors found.

Django version 0.97-pre, using settings ‘pybookmarks.settings’
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Projet django initial

voir:Pour la base de données j’ai dû choisir le site “ftp://sunsite.dk/projects/cygwinports” pour pouvoir installer la base de données sqlite3. Pour vérifier que la base sqlite3 est opérationnelle je fais:

$ python
Python 2.5.1 (r251:54863, Jun 19 2007, 22:55:07)
[GCC 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)] on cygwin
Type « help », « copyright », « credits » or « license » for more information.
>>> import sqlite3
>>>

Modication du fichier /cygdrive/e/projets/pybookmarks/settings.py

modification du fichier applicatif setting.py

 pve@pc72 /cygdrive/e/projets/pybookmarks
$ python manage.py syncdb
Creating table auth_message
Creating table auth_group
Creating table auth_user
Creating table auth_permission
Creating table django_content_type
Creating table django_session
Creating table django_site

You just installed Django’s auth system, which means you don’t have any superuse
rs defined.
Would you like to create one now? (yes/no): yes
Username (Leave blank to use ‘pve’):
E-mail address: pvergain@gmail.com
Password:
Password (again):
Superuser created successfully.
Installing index for auth.Message model
Installing index for auth.Permission model
Loading ‘initial_data’ fixtures…
No fixtures found.

# Création de notre application
$ date; python manage.py startapp bookmarks; date
Fri Jul 13 13:03:58     2007
Fri Jul 13 13:04:00     2007

pve@pc72 /cygdrive/e/projets/pybookmarks
$ ls -als bookmarks/
total 32
0 drwxr-xr-x 2 pve mkgroup-l-d  0 Jul 13 13:04 .
0 drwxr-xr-x 3 pve mkgroup-l-d  0 Jul 13 09:03 ..
0 -rw-r–r– 1 pve mkgroup-l-d  0 Jul 13 13:04 __init__.py
16 -rw-r–r– 1 pve mkgroup-l-d 57 Jul 13 13:04 models.py
16 -rw-r–r– 1 pve mkgroup-l-d 26 Jul 13 13:04 views.py

Modification du modèle de données (models.py)


     Le modèle de données

ATTENTION: ne pas oublier le codage dans l’entête du fichier + la méthode __unicode__ non décrite dans le bouquin !

#!/usr/bin/python
# -*- coding: UTF-8 -*-

pve@pc72 /cygdrive/e/projets/pybookmarks
$ python manage.py sql bookmarks
BEGIN;
CREATE TABLE « bookmarks_bookmark » (
« id » integer NOT NULL PRIMARY KEY,
« user_id » integer NOT NULL,
« url » varchar(200) NOT NULL,
« short_description » varchar(255) NOT NULL,
« long_description » text NOT NULL,
« timestamp » datetime NOT NULL,
« public » bool NOT NULL
)
;
CREATE TABLE « bookmarks_tag » (
« id » integer NOT NULL PRIMARY KEY,
« name » varchar(100) NOT NULL
)
;
CREATE TABLE « bookmarks_bookmark_tags » (
« id » integer NOT NULL PRIMARY KEY,
« bookmark_id » integer NOT NULL REFERENCES « bookmarks_bookmark » (« id »),
« tag_id » integer NOT NULL REFERENCES « bookmarks_tag » (« id »),
UNIQUE (« bookmark_id », « tag_id »)
)
;
COMMIT;

If you’re interested, also run the following commands:
  • python manage.py validate pybookmarks — Checks for any errors in the construction of your models.
  • python manage.py sqlcustom pybookmarks — Outputs any custom SQL statements (such as table modifications or constraints) that are defined for the application.
  • python manage.py sqlclear pybookmarks — Outputs the necessary DROP TABLE statements for this app, according to which tables already exist in your database (if any).
  • python manage.py sqlindexes pybookmarks — Outputs the CREATE INDEX statements for this app.
  • python manage.py sqlall pybookmarks — A combination of all the SQL from the ‘sql’, ‘sqlcustom’, and ‘sqlindexes’ commands.

Looking at the output of those commands can help you understand what’s actually happening under the hood.

Looking at the output of those commands can help you understand what’s actually happening under the hood.

$ python manage.py syncdb
Creating table auth_message
Creating table auth_group
Creating table auth_user
Creating table auth_permission
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table bookmarks_bookmark
Creating table bookmarks_tag

You just installed Django’s auth system, which mean
rs defined.
Would you like to create one now? (yes/no): yes
Username (Leave blank to use ‘pve’):
E-mail address: pvergain@gmail.com
Password:
Password (again):
Superuser created successfully.
Installing index for auth.Message model
Installing index for auth.Permission model
Installing index for bookmarks.Bookmark model
Installing index for bookmarks.Tag model
Loading ‘initial_data’ fixtures…
No fixtures found.

Writing your first Django app, part 2 (http://www.djangoproject.com/documentation/tutorial02/)

Generating admin sites for your staff or clients to add, change and delete content is tedious work that doesn’t require much creativity. For that reason, Django entirely automates creation of admin interfaces for models.

Django was written in a newsroom environment, with a very clear separation between “content publishers” and the “public” site. Site managers use the system to add news stories, events, sports scores, etc., and that content is displayed on the public site. Django solves the problem of creating a unified interface for site administrators to edit content.

The admin isn’t necessarily intended to be used by site visitors; it’s for site managers.

Add "django.contrib.admin" to your INSTALLED_APPS setting.

INSTALLED_APPS = (
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.sites’,
‘pybookmarks.bookmarks’,
‘django.contrib.admin’,
)

Run python manage.py syncdb. Since you have added a new application to INSTALLED_APPS, the database tables need to be updated.

$ python manage.py syncdb
Creating table django_admin_log
Installing index for admin.LogEntry model
Loading ‘initial_data’ fixtures…
No fixtures found.

Edit your mysite/urls.py file and uncomment the line below “Uncomment this for admin:”. This file is a URLconf; we’ll dig into URLconfs in the next tutorial. For now, all you need to know is that it maps URL roots to applications.

Recall from Tutorial 1 that you start the development server like so:

python manage.py runserver

Now, open a Web browser and go to “/admin/” on your local domain — e.g., http://127.0.0.1:8000/admin/. You should see the admin’s login screen:

Administration django

 

Just one thing to do: We need to specify in the bookmarks model that Bookmark objects have an admin interface. Edit the pybookmarks/bookmarks/models.py file and make the following change to add an inner Admin class:

The class Admin will contain all the settings that control how this model appears in the Django admin. All the settings are optional, however, so creating an empty class means “give this object an admin interface using all the default options.

Now reload the Django admin page to see your changes. Note that you don’t have to restart the development server — the server will auto-reload your project, so any modifications code will be seen immediately in your browser.

 Et ça marche !

 Admin bookmarks

Saisie d’un bookmark

Things to note here:

  • The form is automatically generated from the Poll model.
  • The different model field types (models.DateTimeField, models.CharField) correspond to the appropriate HTML input widget. Each type of field knows how to display itself in the Django admin.
  • Each DateTimeField gets free JavaScript shortcuts. Dates get a “Today” shortcut and calendar popup, and times get a “Now” shortcut and a convenient popup that lists commonly entered times.

Writing your first Django app, part 3 (http://www.djangoproject.com/documentation/tutorial03/)

Installation de dateutil: d’abord le télécharger, le décompresser, aller sous le répertoire python-dateutil et faire ‘python setup.py install’

$ python setup.py install
running install
running build
running build_py
creating build
creating build/lib
creating build/lib/dateutil
copying dateutil/__init__.py -> build/lib/dateutil
copying dateutil/easter.py -> build/lib/dateutil
copying dateutil/parser.py -> build/lib/dateutil
copying dateutil/relativedelta.py -> build/lib/dateutil
copying dateutil/rrule.py -> build/lib/dateutil
copying dateutil/tz.py -> build/lib/dateutil
copying dateutil/tzwin.py -> build/lib/dateutil
creating build/lib/dateutil/zoneinfo
copying dateutil/zoneinfo/__init__.py -> build/lib/dateutil/zoneinfo
running install_lib
creating /usr/lib/python2.5/site-packages/dateutil
copying build/lib/dateutil/__init__.py -> /usr/lib/python2.5/site-packages/dateutil
copying build/lib/dateutil/easter.py ->/usr/lib/python2.5/site-packages/dateutil
copying build/lib/dateutil/parser.py -> /usr/lib/python2.5/site-packages/dateutil
copying build/lib/dateutil/relativedelta.py -> /usr/lib/python2.5/site-packages/dateutil
copying build/lib/dateutil/rrule.py -> /usr/lib/python2.5/site-packages/dateutil

copying build/lib/dateutil/tz.py -> /usr/lib/python2.5/site-packages/dateutil
copying build/lib/dateutil/tzwin.py -> /usr/lib/python2.5/site-packages/dateutil

creating /usr/lib/python2.5/site-packages/dateutil/zoneinfo
copying build/lib/dateutil/zoneinfo/__init__.py -> /usr/lib/python2.5/site-packages/dateutil/zoneinfo
byte-compiling /usr/lib/python2.5/site-packages/dateutil/__init__.py to __init__
.pyc
byte-compiling /usr/lib/python2.5/site-packages/dateutil/easter.py to easter.pyc
byte-compiling /usr/lib/python2.5/site-packages/dateutil/parser.py to parser.pyc
byte-compiling /usr/lib/python2.5/site-packages/dateutil/relativedelta.py to relativedelta.pyc
byte-compiling /usr/lib/python2.5/site-packages/dateutil/rrule.py to rrule.pyc
byte-compiling /usr/lib/python2.5/site-packages/dateutil/tz.py to tz.pyc
byte-compiling /usr/lib/python2.5/site-packages/dateutil/tzwin.py to tzwin.pyc
byte-compiling /usr/lib/python2.5/site-packages/dateutil/zoneinfo/__init__.py to
__init__.pyc
running install_data
copying dateutil/zoneinfo/zoneinfo-2007f.tar.gz -> /usr/lib/python2.5/site-packages/dateutil/zoneinfo
running install_egg_info
Writing /usr/lib/python2.5/site-packages/python_dateutil-1.2-py2.5.egg-info

# On vérifie que ça marche
$ python
Python 2.5.1 (r251:54863, Jun 19 2007, 22:55:07)
[GCC 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)] on cygwin
Type « help », « copyright », « credits » or « license » for more information.

>>> import dateutil.parser
>>>
La suite la semaine prochaine.

http://www.djangosites.org/latest/

http://www.djangobook.com/

http://www.djangoproject.com/

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

 
%d blogueurs aiment cette page :