Dynamic Web Development with Seaside

24.3.2Content Type

Using HTTP and Seaside-REST, we can also specify the format of the data that is requested or sent. To do that we use the Accept header of the HTTP request. Depending on it, the REST web service will adapt itself and provide the corresponding data type. We will take the previous example and we will modify it so that it serves the list of todo items not only as text, but also as JSON or XML. To do so define two new methods named listJson and listXml. Both methods will be a GET request, but additionally we annotate them with the mime type they produce using <produces: 'mime-type'>. This annotation specifies the type of the data returned by the method. A structured format like XML and JSON is friendly to other applications that would like to read the output.

ToDoHandler>>listJson
<get>
<produces: 'text/json'>

^ (Array streamContents: [ :stream |
ToDoList default items do: [ :each |
stream nextPut: (Dictionary new
at: 'title' put: each title;
at: 'done' put: each done;
yourself) ] ])
asJavascript
ToDoHandler>>listXml
<get>
<produces: 'text/xml'>

^ WAXmlCanvas builder
documentClass: WAXmlDocument;
render: [ :xml |
xml tag: 'items' with: [
ToDoList default items do: [ :each |
xml tag: 'item' with: [
xml tag: 'title' with: each title.
xml tag: 'due' with: each due ] ] ] ]

While in the examples above we (mis)use the JSON and XML builders that come with our Seaside image. You might want to use any other framework or technique to build your output strings.

By specifying the accept-header we can verify that our implementation serves the expected implementations:

$ curl -H "Accept: text/json" http://localhost:8080/todo-api
[{"title": "Finish todo app chapter", "done": false},
 {"title": "Annotate first chapter", "done": true},
 {"title": "Discuss cover design", "done": false},
 {"title": "Give REST a try", "done": true}]
$ curl -H "Accept: text/xml" http://localhost:8080/todo-api
<items>
  <item>
    <title>Finish todo app chapter</title>
    <done>false</done>
  </item>
  <item>
    ... 

If the accept-header is missing or unknown, our old textual implementation is called. This illustrates that several methods can get a get annotation and that one is selected and executed depending on the information available in the request. We explain this point later.

Similarly the client can specify the MIME type of data passed to the server using the content-type header. Such behavior only makes sense with PUT and POST requests and is specified using the <consumes: 'mime-type'> annotation. The following example states that the data posted to the server is encoded as JSON.

ToDoHandler>>createJson
<post>
<consumes: '*/json'>

| json |
json := JSJsonParser parse: self requestContext request rawBody.
ToDoList default items
add: (ToDoItem new
title: (json at: 'title');
done: (json at: 'done' ifAbsent: [ false ]);
yourself).
^ 'OK'

We can test the implementation with the following cURL query:

$ curl -H "Content-Type: text/json" \
   -d '{"title": "Check out latest Seaside"}' \
   http://localhost:8080/todo-api
OK

Copyright © 19 March 2024 Stéphane Ducasse, Lukas Renggli, C. David Shaffer, Rick Zaccone
This book is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 license.

This book is published using Seaside, Magritte and the Pier book publishing engine.