Top Banner
Frameworkless Web Development in Clojure Andreas ’Kungi’ Klein 24.01.2015 Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 1 / 21
21
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Frameworkless Web Development in Clojure

Frameworkless Web Development in Clojure

Andreas ’Kungi’ Klein

24.01.2015

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 1 / 21

Page 2: Frameworkless Web Development in Clojure

Outline

1 Web programming in Clojure

2 HTTP Abstraction

3 Routing

4 HTML Templating

5 Authentication

6 Conclusion

7 Bibliography

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 2 / 21

Page 3: Frameworkless Web Development in Clojure

Web programming in Clojure

What’s I like?No dominant web frameworksNo framework dependent paradigmsJust plain clojure data structures and functionsThe full power of clojure is available in every ”step”

Web programming from the bottom up

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 3 / 21

Page 4: Frameworkless Web Development in Clojure

What’s nice to have for web programming?

The typical web framework offers:Web Server / HTTP abstractionMiddlewares

SessionCSRF Protection

Request RoutingHTML TemplatingAuthentication(Database abstraction)(Communication with the frontend)

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 4 / 21

Page 5: Frameworkless Web Development in Clojure

Clojure’s libraries

Feature LibraryHTTP Abstraction RingMiddlewares RingRouting Clout / CompojureHTML Templating Hiccup / EnliveAuthentication Friend

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 5 / 21

Page 6: Frameworkless Web Development in Clojure

What is Ring?

Ring is the most widely used Clojure interface between web servers and webapplications.Similar technologies in other languages include

Plack (Perl)Rack (Ruby)Clack (Common Lisp)WSGI (Python)

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 6 / 21

Page 7: Frameworkless Web Development in Clojure

Ring concepts

Client

Adapter

HTTP Request

Ring Request

Middleware

Ring RequestHandler

Adapter

HTTP Response

Ring Response

Middleware

Ring Response

Requests, Responses, Middlewares, Handlers, AdaptersAndreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 7 / 21

Page 8: Frameworkless Web Development in Clojure

Ring exampleExample project.clj:dependencies [[org.clojure/clojure "1.6.0"]

[ring/ring-core "1.3.2"]]:plugins [[lein-ring "0.9.1"]]:ring {:handler ring-example.core/handler}

Example Ring handler(ns ring-example.core)

(defn handler [request]{:status 200:headers {"Content-Type" "text/html"}:body "Hello World"})

Start withlein ring server

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 8 / 21

Page 9: Frameworkless Web Development in Clojure

Request / Response

Requests and responses are maps (see ring spec)(require '[ring.mock.request :as mock])(mock/request :get "/hallo")=> {:server-port 80

:server-name "localhost":remote-addr "localhost":uri "/hallo":query-string nil:scheme :http:request-method :get:headers {"host" "localhost"}}

(handler (mock/request :get "/hallo"))=> {:headers {"Content-Type" "text/html"}

:status 200:body "Hello World"}

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 9 / 21

Page 10: Frameworkless Web Development in Clojure

Middleware

A middleware is a higher order function that enhances a handler. The firstargument is a handler-function and the middleware itself returns a handlerfunction.(defn wrap-content-type [handler content-type]

(fn [request](let [response (handler request)]

(assoc-in response [:headers "Content-Type"] content-type))))

Usage(def app

(-> handler(wrap-content-type "text/html")))

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 10 / 21

Page 11: Frameworkless Web Development in Clojure

Adapter

Adapters translate from a concrete web server to ring requests and responses. Forexample ring.adapter.jetty as the name suggests connects ring to jetty. Thisis where most of the magic happens:(defn- proxy-handler

"Returns an Jetty Handler implementationfor the given Ring handler."[handler](proxy [AbstractHandler] []

(handle [_ ^Request base-request request response](let [request-map (servlet/build-request-map request)

response-map (handler request-map)](when response-map

(servlet/update-servlet-response responseresponse-map)

(.setHandled base-request true))))))

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 11 / 21

Page 12: Frameworkless Web Development in Clojure

Clout

Clout is an HTTP matching library using the same syntax as Rails or Sinatra.(require '[clout.core :as clout])(require '[ring.mock.request :as mock])

(clout/route-matches "/article/:title/author/:name"(mock/request :get "/article/clojure/author/rich"))

=> {:title "clojure" :name "rich"}

(clout/route-matches "/article/:title"(mock/request :get "schnuff-schnuff"))

=> nil

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 12 / 21

Page 13: Frameworkless Web Development in Clojure

Compojure

Most of the time Clout is used through compojure. Compojure combines ring andclout through a concise syntax.(require '[compojure.core :refer [GET POST defroutes]])(require '[ring.util.response :refer [redirect-after-post]])

(defroutes app(GET "/article/:title" [title]

(str "Found article " title))(POST "/article" request

(let [article (create-article (:body request))](reqirect-after-post (str "/article/" (:id article))))))

Compojure routes are ring handlers.

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 13 / 21

Page 14: Frameworkless Web Development in Clojure

Hiccup

Hiccup is a literal translation from HTML to clojure vectors and maps.(use 'hiccup.page)(html5[:head [:title "How much do I love clojure?"]][:body [:h1 "Very Much!"]])

=> "<!DOCTYPE html><html>

<head><title>How much do I love clojure?</title></head><body><h1>Very Much!</h1></body>

</html>"It also provides some CSS selector like shortcuts.[:div#id.class-1.class2 "Schnuff!"]=> "<div class=\"class-1 class2\" id=\"id\">Schnuff!</div>"

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 14 / 21

Page 15: Frameworkless Web Development in Clojure

EnliveA completely different strategy to HTML templating is provided by Enlive. Itworks with selectors and transformations.Suppose the following HTML exists in test.html:<!DOCTYPE html><html lang="en">

<head><title>This is a title placeholder</title></head><body><ul></ul></body>

</html>

(use '[net.cgrand.enlive-html])

(deftemplate main-template "test.html"[foo][:head :title] (content (str "Enlive starter kit: " foo))[:body :ul] (do-> (add-class "item-list")

(append (html [:li "Item 1"]))(append (html [:li "Item 2"]))))

deftemplate creates a function which returns html.

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 15 / 21

Page 16: Frameworkless Web Development in Clojure

Friend

Friend calls itself an ”extensible authentication and authorization library forClojure Ring web applications and services.”

Works very well with ring/compojureProvides role based authentication

Friend contains two mayor conceptsCredential functionsAuthentication workflows (e.g. OAuth, HTTP Basic Auth, Login Form, …)

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 16 / 21

Page 17: Frameworkless Web Development in Clojure

Credential function example

A credential function returns either nil or a map representing the credentials like

{:username "joe":app.foo/passphrase "bcrypt hash"}

(defn bcrypt-credential-fn[load-credentials-fn {:keys [username password]}](when-let [creds (load-credentials-fn username)]

(let [password-key (or (-> creds meta ::password-key):password)]

(when (bcrypt-verify password (get creds password-key))(dissoc creds password-key)))))

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 17 / 21

Page 18: Frameworkless Web Development in Clojure

Workflow exampleA workflow function either returns an authorization map containing the userscredentials or it delivers an error page depending on the kind of workflow. Theinteractive-form workflow for example delivers a login page in case of failure.See the following simplified example from the friend source:(defn http-basic

[& {:keys [credential-fn realm] :as basic-config}](fn [{{:strs [authorization]} :headers :as request}](when (and authorization (re-matches #"\s*Basic\s+(.+)" authorization))

(if-let [[[_ username password]] (extract-username-password ...)](if-let [user-record

((gets :credential-fnbasic-config(::friend/auth-config request))

^{::friend/workflow :http-basic}{:username username, :password password})]

(make-auth user-record{::friend/workflow :http-basic::friend/redirect-on-auth? false::friend/ensure-session false})

(http-basic-deny realm request)){:status 400:body "Malformed Authorization header for HTTP Basic authentication."}))))

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 18 / 21

Page 19: Frameworkless Web Development in Clojure

Putting it together(ns your.ring.app

(:require [cemerick.friend :as friend](cemerick.friend [workflows :as workflows]

[credentials :as creds])))

;; a dummy in-memory user "database"(def users {"root" {:username "root"

:password (creds/hash-bcrypt "admin_password"):roles #{::admin}}

"jane" {:username "jane":password (creds/hash-bcrypt "user_password"):roles #{::user}}})

(def ring-app;; ... assemble routes however you like ...)

(def secured-app(-> ring-app

(friend/authenticate{:credential-fn (partial creds/bcrypt-credential-fn users):workflows [(workflows/interactive-form)]})

;; ...required Ring middlewares ...))

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 19 / 21

Page 20: Frameworkless Web Development in Clojure

Frameworkless Web Development

What’s great about Frameworkless?Small libraries can be composed easilyCommunication between these libraries is easy because plain old clojure datastructures are usedSystems become easier through separation of concerns

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 20 / 21

Page 21: Frameworkless Web Development in Clojure

Bibliography

https://github.com/ring-clojure/ringhttps://github.com/weavejester/clouthttps://github.com/weavejester/compojurehttps://github.com/weavejester/hiccuphttps://github.com/cgrand/enlivehttps://github.com/cemerick/friend

Andreas ’Kungi’ Klein Frameworkless Web Development in Clojure 24.01.2015 21 / 21