Compojure w akcji - trasowanie i obsługa parametrów

Z Jacek Laskowski - Wiki Projektanta Java EE

W artykule Clojure w aplikacji webowej z Compojure oraz Ring i Hiccup przedstawiłem szkielet aplikacji webowych Compojure zbudowany na języku Clojure. Compojure jest w tak aktywnym rozwoju, że mimo wersji 0.4.1 już dorobił się kilku bibliotek wspierających - hiccup oraz clout, a sam jest nakładką dla innej biblioteki ring. Nie stanowią one bardzo obszernych rozwiązań - szkieletów aplikacyjnych, do których przyzwyczaiłem się w Java EE i zrozumienie sensu istnienia wspomnianych stanowi dla mnie nie lada wyzwanie. A gdzie tu jeszcze umiejętne użycie.

W tym odcinku-artykule w serii o roboczej nazwie "Poznajemy Clojure praktycznie" przedstawię sposób, w jaki Compojure obsługuje trasowanie (wyznaczanie tras, tj. routing) oraz parametry z formularzy (aczkolwiek w tym odcinku formularza nie będzie i będzie jedynie przekazywanie parametrów przez adres URL).

Zakładam, że czytelnik jest zaznajomiony z treścią wspomnianego wcześniej artykułu Clojure w aplikacji webowej z Compojure oraz Ring i Hiccup i pracuje ze stworzonym tam projektem aplikacji webowej.

Spis treści

Plik konfiguracyjny projektu - project.clj

Modyfikujemy plik konfiguracyjny projektu project.clj jak poniżej.

(defproject pl.jaceklaskowski.clojure/witaj-clojure-webapp "1.0.0-SNAPSHOT"
  :description "Aplikacja webowa typu 'Witaj Świecie' w Clojure z Compojure"
  :url "http://jaceklaskowski.pl"
  :main pl.jaceklaskowski.clojure.witaj-clojure-webapp.core
  :dependencies [[org.clojure/clojure "1.2.0"]
                 [org.clojure/clojure-contrib "1.2.0"]
                 [compojure "0.4.1"]
                 [hiccup "0.2.7"]
                 [ring/ring-jetty-adapter "0.3.0"]]
  :dev-dependencies [[swank-clojure "1.3.0-SNAPSHOT"]])

Zmiany dotyczą wyłącznie wersji bibliotek.

Pobieranie zależności - lein deps

Zmieniając plik konfiguracyjny projektu project.clj zdefiniowaliśmy nowe zależności, które pobieramy za pomocą polecenia lein deps.

devmac:witaj-clojure-webapp jacek$ lein deps
Copying 15 files to /Users/jacek/sandbox/witaj-clojure-webapp/lib
Overriding previous definition of reference to witaj-clojure-webapp
Overriding previous definition of reference to dependency.fileset
Copying 2 files to /Users/jacek/sandbox/witaj-clojure-webapp/lib/dev

Edycja głównego pliku aplikacji Compojure - pl.jaceklaskowski.clojure.witaj_clojure_webapp/core.clj

Modyfikujemy plik src/pl.jaceklaskowski.clojure.witaj_clojure_webapp/core.clj, tak aby zawierał następującą treść:

(ns pl.jaceklaskowski.clojure.witaj-clojure-webapp.core
  "Bardzo prosta(cka) aplikacja webowa w Compojure"
  (:use [compojure.core]
        [hiccup core form-helpers page-helpers]
        [ring.adapter.jetty :only [run-jetty]])
  (:require [compojure.route :as route]))
 
(defn view-layout [& content]
  (xhtml {:lang "en"}
    [:head [:title "Compojure in Action"]]
    [:body content]))
 
(defn display [name]
  (html [:h1 (str "Witaj " name ", tu Clojure z Compojure!")]))
 
(defn view-output [name]
  (view-layout
    (display name)))
 
(defroutes app
  "Tablica tras dla aplikacji webowej"
  (GET "/user/:name" [name] (view-output name))
  (ANY "/*" [] "<b>Page not found</b>"))
 
(defonce server (run-jetty app
                           {:join? false
                            :port 8080}))

I tu właśnie pojawiają się największe zmiany w aplikacji. Zacznijmy od samej góry.

Najpierw deklarujemy wykorzystywane "pakiety" - przestrzenie nazewnicze Clojure dla Compojure, Hiccup oraz Ring. W formie view-layout definiujemy strukturę docelowej strony, która zostanie wyświetlona użytkownikowi. Składa się ona z nagłówka z tytułem "Compojure in Action" oraz podaną na wejściu treścią. Każda tablica (struktura pomiędzy []) definiuje znacznik HTML w postaci ":znacznik" (dwukropek, po którym następuje nazwa znacznika". Funkcja display opiera swoje działanie na makrze html (część hiccup.core) i podobnie jak przy view-layout struktura HTML odpowiada zagnieżdżonym tablicom ze znacznikami (z dwukropkiem). Funkcja display przyjmuje pojedynczy parametr wejściowy. Funkcja view-output składa poprzednie i jej zrozumienie nie powinno stanowić problemu.

Najważniejszą częścią aplikacji opartej na Compojure jest zbudowanie trasowania za pomocą makra defroutes, które w połączeniu z funkcją run-jetty (ostatnia linia w pliku) określa, które metody HTTP, np. GET oraz adresy są obsługiwane i jak. W naszym przypadku żądanie GET dla adresu /user z opcjonalnym parametrem name powoduje wywołanie funkcji view-output. Każde inne żądanie jest obsługiwane przez makro ANY, które po prostu wyświetli wytłuszczony ciąg znaków Page not found.

Więcej informacji nt. trasowania w Compojure można znaleźć w dokumencie Routes In Detail.

Uruchomienie aplikacji webowej

Uruchamiamy aplikację za pomocą polecenia lein repl.

devmac:witaj-clojure-webapp jacek$ lein repl
"REPL started; server listening on localhost:1635."
2010-09-20 20:44:55.094::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
2010-09-20 20:44:55.095::INFO:  jetty-6.1.14
2010-09-20 20:44:55.112::INFO:  Started SocketConnector@0.0.0.0:8080
pl.jaceklaskowski.clojure.witaj-clojure-webapp.core=>

Teraz wystarczy jedynie uruchomić przeglądarkę i przetestować działanie aplikacji.

Rozpoczynamy od przypadku poprawnego podając adres http://localhost:8080/user/jacek.

Plik:compojure-trasowanie-witaj-jacku.png

Kolejny test to wykonanie dowolnego innego adresu, np. wejście na adres http://localhost:8080.

Plik:compojure-trasowanie-page-not-found.png

Kończy się niepowodzeniem, co było oczekiwane.

W kolejnym odcinku zaplanowałem prezentację obsługi formularza. Do następnego razu!

Osobiste