Clojure Web 开发 (一)
使用Clojure做Web开发需要的工具链还是比较特殊的,本文主要描述一下其中牵涉到的框架。
需要指出的是,Clojure可以把自己封装成jar包供java调用,因此最坏情况下还是可以用java servlet来调用Clojure的库的,但是这显然不是我想要达到的效果,因此本文的基本出发点是,核心语言用Clojure,需要调用java库的情况。
Ring是一个Clojure Web程序库,有点类似与Java的servlet。为什么要用Ring呢?Wiki里面列了几个好处:
- Write your application using Clojure functions and maps
- Run your application in a auto-reloading development server
- Compile your application into a Java servlet
- Package your application into a Java war file
- Take advantage of a large selection of pre-written middleware
- Deploy your application in cloud environments like Amazon Elastic Beanstalk and Heroku
事实上,Ring是大部分Clojure的Web Framework的基础。包括了:Compojure、Moustache和Noir。
真正在开始用Ring之前,首先要介绍的就是Leiningen。这个玩意也是个挺牛b的东西,而且作者也比较有意思,用户问答文档里面全面嘲笑了Ant和Maven,虽然Leiningen就是基于他们的。按照我现在的理解,基本上,用这个玩意,只要你指定好了依赖关系,它就会自动下载对应的库,并且放在正确的地方,然后还可以编译你的项目,执行你的项目。下面我们用的所有库都是用Leiningen实现的。
-------------
RING
使用leiningen创建一个项目:
lein new hello-world
cd hello-world
紧接着就要修改project.clj,指定依赖关系。
(defproject hello-world "1.0.0-SNAPSHOT"
:description "FIXME: write"
:dependencies [[org.clojure/clojure "1.2.0"]
[org.clojure/clojure-contrib "1.2.0"]
[ring/ring-core "0.3.4"]
[ring/ring-jetty-adapter "0.3.4"]])
然后调用deps来告诉leiningen下载需要的数据:
lein deps
接着修改项目文件夹src目录下的core.clj文件如下:
(ns hello-world.core)
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/html"}
:body "Hello World"})
之后便可以调用
lein repl
调用出REPL,接着就可以在命令行中启动服务器
=> (use 'ring.adapter.jetty)
=> (use 'hello-world.core)
=> (run-jetty handler {:port 3000})
在Ring的抽象中,一个web程序分为4个部分,Handler、Request、Response、MiddleWare
其中Handler即为定义web程序的函数,该函数只有一个参数:一个代表了Http请求的Map,最后返回一个代表HTTP返回值的map。比如:
(defn what-is-my-ip [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body (:remote-addr request)})
Request也就是http请求也是一个Map,下面主要描述了其自带的key的情况。在ring中,可以使用middleware添加用户自定义的key:
- :server-port The port on which the request is being handled.
- :server-name The resolved server name, or the server IP address.
- :remote-addr The IP address of the client or the last proxy that sent the request.
- :uri The request URI (the full path after the domain name).
- :query-string The query string, if present.
- :scheme The transport protocol, either :http or :https.
- :request-method The HTTP request method, which is one of :get, :head, :options, :put, :post, or :delete.
- :content-type The MIME type of the request body, if known.
- :content-length The number of bytes in the request body, if known.
- :character-encoding The name of the character encoding used in the request body, if known.
- :headers A Clojure map of lowercase header name strings to corresponding header value strings.
- :body An InputStream for the request body, if present.
- :status The HTTP status code, such as 200, 302, 404 etc.
- :headers A Clojure map of HTTP header names to header values. These values may either be strings, in which case one name/value header will be sent in the HTTP response, or a collection of strings, in which case a name/value header will be sent for each value.
- :body A representation of the response body, if a response body is appropriate for the response's status code. The body can be one of four types:
- String The body is sent directly to the client.
- ISeq Each element of the seq is sent to the client as a string.
- File The contents of the referenced file is sent to the client.
- InputStream The contents of the stream is sent to the file. When the stream is exhausted, the stream is closed.
(defn wrap-content-type [handler content-type]
(fn [request]
(let [response (handler request)]
(assoc-in response [:headers "Content-Type"] content-type))))
(response "Hello World")
(-> (response "Hello World")
(content-type "text/plain"))
(redirect "http://example.com")
(file-response "readme.html" {:root "public"})
(resource-response "readme.html" {:root "public"})
ring也支持cookie和session。最重要的是Ring支持交互式的开发,这个也是Lisp的强项。
进行交互式开发的三种方法,我们这里主要引述一下使用lein-ring插件的方法:
1,首先在project.clj文件中指明:
:dev-dependencies [[lein-ring "0.4.5"]]
2, 运行lein deps,下载对应的库
3,在Project.clj文件最后添加:
:ring {:handler your-app.core/handler}))
4,调用下面命令启动服务器:
lein ring server
5,该服务器会自动重新加载src中任何修改过的文件。
介绍了Ring之后,我们需要了解,大部分情况下我们不需要在Ring之上写web程序,因为已经有人帮我们弄了一些框架,这些框架能够极大的简化开发流程,这些框架中比较流行的包括:
- Compojure: A concise DSL to generate Ring handler functions
- Moustache: A micro DSL to wire Ring handlers and middlewares
- fjord: A minimal Ring app framework
- clocks: Clojure Web DSL build on top of compojure, ring and scripture
- Ringfinger: RESTful resources with multiple outputs (HTML, XML, JSON), database and email abstraction, pre-made authorization system (form-based + HTTP Basic), security middleware, etc.
- Noir: A micro-framework that allows you to rapidly develop websites in Clojure.