четверг, 19 мая 2011 г.

Сессия в Ring


Продолжая тему web-разработки на Clojure, расскажу об использовании сессий в Ring. С наскоку разобраться в этой теме чуть сложнее, чем с той же маршрутизацией, поэтому рассмотрим реализацию сессий в подробностях.
Вначале, разберемся, что такое request и response в Ring. Вообще, request и response – это обычный map, который Ring возвращает в пользовательскую функцию. В этом легко убедиться, добавив в маршруты (здесь я использую Compojure):
(defroutes main-routes
  . . .
  (GET "/request" request (str request))
  . . .)
Теперь если набрать в нашем веб-приложении адрес /request, то в броузере будет примерно следующее: 

{:remote-addr "127.0.0.1", :scheme :http, :query-params {}, :session {}, 
:form-params {}, :multipart-params {}, :request-method :get, :query-string nil, 
:route-params {}, :content-type nil, :cookies {"ring-session" {:value 
"7d2c9094-b6d0-4dd3-8f0d-5457cb675fbf"}}, :uri "/request", 
:server-name "127.0.0.1", :params {}, :headers {"user-agent" "Mozilla/5.0 (X11; 
Linux i686; rv:2.0) Gecko/20100101 Firefox/4.0", "cookie" 
"ring-session=7d2c9094-b6d0-4dd3-8f0d-5457cb675fbf", "keep-alive" "115", 
"accept-charset" "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "accept" 
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "host" 
"127.0.0.1:3000", "cache-control" "no-cache", "accept-encoding" "gzip, deflate", 
"pragma" "no-cache", "accept-language" "en-us,en;q=0.5", "connection" "keep-alive"},
:content-length nil, :server-port 3000, :character-encoding nil, 
:body #<Input org.mortbay.jetty.HttpParser$Input@1590164>}

В ключе :session как раз и хранится сессия. Полученную через метод GET сессию можно прочитать, а отправленную в response – записать.
Добавим следующие маршруты:

(defroutes main-routes
  . . .
  (GET "/test-get" {params :params, session :session} (my-test-get params session))
  (POST "/test-post" {params :params, session :session} (my-test-post params session))
  . . .)
И реализуем функции:
(defn with-session [session response]
  (merge response
     {:session
      (merge (response :session)
         session)}))

(defn my-test-get [params session]
  (str "<html><body>"
(:my session) 
"<form action=\"/test-post\" method=\"post\">
<input type=\"submit\" value=\"Submit\" />
</form>
</body></html>"))

(defn my-test-post [params session]
  (let [old (session :my)
    new (str old "@")]
    (with-session {:my new}
      (redirect-to "/test-get"))))
Функция my-test-get выводит html-страницу с ключем :my из сессии и пустой формой с кнопкой Submit. Эта кнопка инициирует POST-запрос на адрес /test-post, в результате которого после маршрутизации будет вызвана функция my-test-post. Она, в свою очередь, добавит к существующей сессии в ключ :my дополнительный символ '@' и выполнит редирект на адрес /test-get. Функция with-session заменит старую сессию на новую с новым значением ключа :my.
Остается только добавить, чтобы это работало, необходимо заюзать ring.middleware.session, и изменить функцию app:

(def app
  (-> (handler/site main-routes)
      wrap-session))
Функцию with-session позаимствовал у Михаила Филоненко из его статьи о сессии в Ring.

Комментариев нет:

Отправить комментарий