Web development
by Carlo Sciolla
Ring handles HTTP
ring
GET /home
HTTP/1.1 200 OK
your app
Map in, map out
GET /home HTTP/1.1
User-Agent: curl/7.37.1
Host: 127.0.0.1:4000
Accept: *
{:request-method :get
:url /home HTTP/1.1
:headers {“User-Agent” ...}
...}
application
{:status 200
:body “Hello, world!”
:headers {...}
...}
HTTP/1.1 200 OK
Content-Type: text/plain
Hello, world!
Anatomy of a ring handler
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello World"})
Following the simplicity of the ring abstraction, ring handlers are plain functions. Compare this to a Java
Servlet.
How to run a ring application
(ns my.web
(:use ring.jetty.adapter))
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello World"})
(run-jetty handler 8080)
You can find ring adapters (-> wrappers) for the most common app servers. Ring will dispatch HTTP
requests to your handler.
How to run a ring application
; in project.clj:
:plugins [[lein-ring "0.9.3"]]
:ring {:handler hello-world.core/handler}
$ lein ring server
Started nREPL server on port 4500
2015-04-20 20:19:37.495:INFO:oejs.Server:jetty-7.6.13.v20130916
2015-04-20 20:19:37.548:INFO:oejs.AbstractConnector:Started
SelectChannelConnector@0.0.0.0:8090
Started server on port 8090
An alternative way to start your application is to run it from sources.
How to run a ring application
; in project.clj:
:plugins [[lein-ring "0.9.3"]]
:ring {:handler hello-world.core/handler}
$ lein ring uberwar
Created /Users/skuro/demo/target/demo-1.0-standalone.war
If you need a standard packaging, that’s available also.
Filtering with middleware
(defn wrap-content-type [handler type]
(fn [request]
(let [response (handler request)]
(assoc-in response [:headers “Content-Type”] type))))
(def wrapped
(wrap-content-type handler “text/plain”))
Middleware functions wrap regular handlers to enhance their functionalities. They accept in input a
handler plus other arguments, and must return a new handler. Compare them to Servlet Filters.
Compojure to route requests
(ns hello-world.core
(:require [compojure.core :refer :all]
[compojure.route :as route]))
(defn surname [name]
({"Alfred" "Hitchcock"} name))
(defroutes app
(GET "/:name" [name] (surname name)
(route/not-found "<h1>Page not found</h1>"))
Compojure helps you route request to the correct function depending on the request details. Route
matches are attempted in lexical order.
Compojure to route requests
(defroutes my-api
(context "/api" []
(GET "/people" [] (list-people)
(GET "/people/:name" [name] (describe-person name))))
; the above registers handles for these URLs:
; /api/people
; /api/people/:name
Routes can be grouped together via context, which establishes a common prefix and common
destructured parameters.
Middleware to convert to JSON
(use '[ring.middleware.json :only [wrap-json-response]]
'[ring.util.response :only [response]])
(defn handler [request]
(response {:foo "bar"}))
(def app
(wrap-json-response handler))
An handler can translate automatically your response bodies into JSON, and will also translate the
incoming request bodies into Clojure datastructures.
Questions?
create a JSON-based REST API that provides the following endpoints:
GET /people
Lists the current people. Ex.:
[{"id": 0, "name": "Carlo"}]
GET /people/:name
Returns the details of the selected person
POST /people
Adds a new person. The body is of the form:
{"name": "Carlo"}
Exercise time:
$ lein new compojure folks
A Clojure DSL for HTML
; in your project.clj
:dependencies [[hiccup "1.0.5"]]
user=> (use ‘hiccup.core)
nil
user=> (html [:span {:class "foo"} "bar"])
"<span class="foo">bar</span>"
Hiccup is a library that translates Clojure datastructures into HTML.
introduce a home page to manage people via HTML:
GET /
- The HTML contains the list of the current people
- The HTML contains a form to create a new person (using the POST
endpoint you created)
Exercise time:
Add HTML pages to your app
Demo
ClojureScript
Thanks
Carlo Sciolla CTO
carlo.sciolla@sytac.nl
+31 6 21205911
mail
phone

Clojure Workshop: Web development

  • 1.
  • 2.
    Ring handles HTTP ring GET/home HTTP/1.1 200 OK your app
  • 3.
    Map in, mapout GET /home HTTP/1.1 User-Agent: curl/7.37.1 Host: 127.0.0.1:4000 Accept: * {:request-method :get :url /home HTTP/1.1 :headers {“User-Agent” ...} ...} application {:status 200 :body “Hello, world!” :headers {...} ...} HTTP/1.1 200 OK Content-Type: text/plain Hello, world!
  • 4.
    Anatomy of aring handler (defn handler [request] {:status 200 :headers {"Content-Type" "text/plain"} :body "Hello World"}) Following the simplicity of the ring abstraction, ring handlers are plain functions. Compare this to a Java Servlet.
  • 5.
    How to runa ring application (ns my.web (:use ring.jetty.adapter)) (defn handler [request] {:status 200 :headers {"Content-Type" "text/plain"} :body "Hello World"}) (run-jetty handler 8080) You can find ring adapters (-> wrappers) for the most common app servers. Ring will dispatch HTTP requests to your handler.
  • 6.
    How to runa ring application ; in project.clj: :plugins [[lein-ring "0.9.3"]] :ring {:handler hello-world.core/handler} $ lein ring server Started nREPL server on port 4500 2015-04-20 20:19:37.495:INFO:oejs.Server:jetty-7.6.13.v20130916 2015-04-20 20:19:37.548:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8090 Started server on port 8090 An alternative way to start your application is to run it from sources.
  • 7.
    How to runa ring application ; in project.clj: :plugins [[lein-ring "0.9.3"]] :ring {:handler hello-world.core/handler} $ lein ring uberwar Created /Users/skuro/demo/target/demo-1.0-standalone.war If you need a standard packaging, that’s available also.
  • 8.
    Filtering with middleware (defnwrap-content-type [handler type] (fn [request] (let [response (handler request)] (assoc-in response [:headers “Content-Type”] type)))) (def wrapped (wrap-content-type handler “text/plain”)) Middleware functions wrap regular handlers to enhance their functionalities. They accept in input a handler plus other arguments, and must return a new handler. Compare them to Servlet Filters.
  • 9.
    Compojure to routerequests (ns hello-world.core (:require [compojure.core :refer :all] [compojure.route :as route])) (defn surname [name] ({"Alfred" "Hitchcock"} name)) (defroutes app (GET "/:name" [name] (surname name) (route/not-found "<h1>Page not found</h1>")) Compojure helps you route request to the correct function depending on the request details. Route matches are attempted in lexical order.
  • 10.
    Compojure to routerequests (defroutes my-api (context "/api" [] (GET "/people" [] (list-people) (GET "/people/:name" [name] (describe-person name)))) ; the above registers handles for these URLs: ; /api/people ; /api/people/:name Routes can be grouped together via context, which establishes a common prefix and common destructured parameters.
  • 11.
    Middleware to convertto JSON (use '[ring.middleware.json :only [wrap-json-response]] '[ring.util.response :only [response]]) (defn handler [request] (response {:foo "bar"})) (def app (wrap-json-response handler)) An handler can translate automatically your response bodies into JSON, and will also translate the incoming request bodies into Clojure datastructures.
  • 12.
  • 13.
    create a JSON-basedREST API that provides the following endpoints: GET /people Lists the current people. Ex.: [{"id": 0, "name": "Carlo"}] GET /people/:name Returns the details of the selected person POST /people Adds a new person. The body is of the form: {"name": "Carlo"} Exercise time: $ lein new compojure folks
  • 14.
    A Clojure DSLfor HTML ; in your project.clj :dependencies [[hiccup "1.0.5"]] user=> (use ‘hiccup.core) nil user=> (html [:span {:class "foo"} "bar"]) "<span class="foo">bar</span>" Hiccup is a library that translates Clojure datastructures into HTML.
  • 15.
    introduce a homepage to manage people via HTML: GET / - The HTML contains the list of the current people - The HTML contains a form to create a new person (using the POST endpoint you created) Exercise time: Add HTML pages to your app
  • 16.
  • 17.