Clojure Web 开发 (一)

使用Clojure做Web开发需要的工具链还是比较特殊的,本文主要描述一下其中牵涉到的框架。

需要指出的是,Clojure可以把自己封装成jar包供java调用,因此最坏情况下还是可以用java servlet来调用Clojure的库的,但是这显然不是我想要达到的效果,因此本文的基本出发点是,核心语言用Clojure,需要调用java库的情况。

首先要介绍的就是Ring    API文档

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.
Response与Reqeust类似,包含了三个Key:
  • :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.
Middleware是一个高阶函数,该函数第一个参数是一个handler,给该函数做一些处理之后,返回一个新的handler函数。比如:
(defn wrap-content-type [handler content-type]
(fn [request]
(let [response (handler request)]
(assoc-in response [:headers "Content-Type"] content-type))))
而在Ring中,通常使用宏:-> 来将多个middleware串联起来,形成:Ring?
Ring中如何产生Response。
Ring提供了ring.util.response来帮助生成一下response。但事实上,我们通常会使用专门的render库来生成返回数据,这里先不表,只列出一下Ring提供的Response函数 (API文档):
(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.
下一篇我们将主要介绍Compojure以及render lib。





posted @ 2011-10-21 23:03  MMJX  阅读(5018)  评论(2编辑  收藏  举报