Chinaunix首页 | 论坛 | 博客
  • 博客访问: 396455
  • 博文数量: 69
  • 博客积分: 1984
  • 博客等级: 上尉
  • 技术积分: 953
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-28 00:43
个人简介

学无所长,一事无成

文章分类

全部博文(69)

文章存档

2015年(19)

2014年(14)

2013年(9)

2012年(17)

2010年(10)

我的朋友

分类: Web开发

2015-06-20 19:09:36



逆旅过客:以下内容为本人学习笔记,是官方文档的缩略版,部分内容或有个人阐释。

一、Getting Started

1、创建一个 project

lein new compojure hello-world
  
 2、启动服务 ,默认端口 3000。源文件如果修改,服务会自动重新加载

cd hello-world 
lein ring server-headless


二、Routes In Detail 

1、compojure 中的 routes 类似如下:

  1. (GET "/user/:id" [id] (str "

    Hello user " id ""))


 routes 返回值为 Ring-handler 函数。

2、匹配 HTTP 方法 -- GET
 
compojure 定义了一系列 route 宏,包括 POST,PUT,DELETE,OPTIONS,PATCH 和 HEAD。如果要匹配任何一种 HTTP request ,可以使用 ANY。注意:这些宏全部都是大写。

3、匹配 URI -- "/user/:id"

这个字符串用于定义 routing(语法来自于 Clout 模块),很多语法格式同 Ruby on Rails 和 Sinatra 一致。
 :id 将匹配 /user/ 后的所有余下部分,结果存入 "id" 参数。
 
如果想定义的更为复杂,可以引入正则表达式:
  1. ["/user/:id", :id #"[0-9]+"]


 compojure 1.3.0 以后引入一种直接嵌入正则表达式的写法(正则表达式用花括号扩起来): 

  1. "/user/:id{[0-9]+}"

 如果 URI 不匹配定义的路径,route 函数将返回 nil。

4、解构 request -- [id]

[id]用于接收数据,这是 GET 宏的第二个参数 ,你可以使用 vector ,也可以使用 Clojure 本身的各种解构数据的方式。
 Clout route 字符串的语法格式会自动将符号 id 绑定到 request map 中的 "id"参数。
 我们当然也可以使用标准的 Clojure 解构 form:
  1. {{id :id} :params}


  
 另外任何时候如果你想要捕获 request map,都可以如下操作:
  1. (GET "/" request (str request))
以上将 request map 转化成字符串作为 response 返回。

更多内容参考:Destructing Syntax part。

 5、Returning a response

 一旦 HTTP 请求匹配上,数据解构完成后,route 函数最后需要的就是一个执行代码块了,同普通函数一样:
  1. (str "

    Hello user " id "")

 
返回值会被自动处理,上面代码返回一个字符串,它会被转化成一个标准的响应:

  1. {:status 200
  2.   :headers {"Content-Type" "text/html; charset=utf-8"}
  3.    :body "

    Hello user 1"}


compojure.response/Renderable protocol 会将上面任意数据(String,map,File 等)转化成合适的 response。你也可以覆写它提供一个你自己类型的定制的渲染器( rendering)。

 6、Combining routes

Compojure 使用 compojure.core/routes 函数来连接 routes.。Each route is attempted in order, until a route is found that returns a non-nil response to the request.


  1. (def my-routes
  2.   (routes
  3.     (GET "/foo" [] "Hello Foo")
  4.     (GET "/bar" [] "Hello Bar")))
以上为通用模式, Compojure 还提供了一个 defroutes 宏:
  1. (defroutes my-routes
  2.   (GET "/foo" [] "Hello Foo")
  3.    (GET "/bar" [] "Hello Bar"))

三、Destructuring Syntax -- 解构语法

Compojure 提供两种方式进行解构:

  1. Clojure 样式 ,就是 let  中使用的那种
  2. Compojue 特定样式,用于从 request map 中解析参数。

常规性的 Clojure 解构

大多数情况下我们会直接捕获 request map:

  1. (GET "/" request
  2.   (str request))
如果你提供了一个 map 或是 symbol,那么就将对 Ring request map 运用Clojure 风格的解构语法 。如下例,展示了如何使用 Clojure 语法将制定参数绑定到一个变量中:
  1. (GET "/:foo" {{foo :foo} :params}
  2.   (str "Foo = " foo))


Compojure 特定样式解构


常规性的解构过于琐碎,Compojure 定制了一种更方便的样式。如果你提供了一个 vector,Compojure 就会使用这种定制语法。下面这个例子就展示了这种更为简洁的写法:

  1. (GET "/:foo" [foo]
  2.   (str "Foo = " foo))

Compojure 的参数解构语法有三部分。第一部分直接将参数绑定到同名的 symbols 上,举个例子,假设我们有如下 request map:
  1. {:request-method :get
  2.  :uri "/foobar"
  3.  :headers {}
  4.  :params {:x "foo", :y "bar", :z "baz", :w "qux"}}

我们可以用一个 symbols 组成的 vector 来绑定每一个参数:
  1. [x y z]
  2. x -> "foo"
  3. y -> "bar"
  4. z -> "baz"

对于剩下的所有未指定参数,我们可以使用 "&" symbol 加一个变量名来进行绑定:

  1. [x y & z]
  2. x -> "foo"
  3. y -> "bar"
  4. z -> {:z "baz", :w "qux"}


这种方式非常类似于 Clojure 中 "&" 的绑定方式。不同之处是 Clojure 中是绑定 list 中余下的部分,得到的还是 list,Compojure 中是绑定 map 中余下的部分,得到的还是 map。 

最后,你还可以使用 :as 关键字将整个 request map 绑定到一个 symbol:

  1. [x y :as r]
  2. x -> "foo"
  3. y -> "bar"
  4. r -> {:request-method :get
  5.       :uri "/foobar"
  6.       :headers {}
  7.       :params {:x "foo", :y "bar", :z "baz", :w "qux"}}


你还可以通过 Clojure 结构 map 后绑定到 :as 关键字:

  1. [x y :as {u :uri}]
  2. x -> "foo"
  3. y -> "bar"
  4. u -> "/foobar"}


四、Nesting routes -- 路由嵌套

context 宏用来给一组 routes 统一加上前缀:

  1. (defroutes user-routes
  2.   (context "/user/current" []
  3.     (GET "/" [] ...) ; 这个 route 实际对应到 "/user/current"
  4.     (GET "/profile" [] ...)
  5.     (GET "/posts" [] ...)))

类似于普通 route ,我们可以给 context 添加 route 参数:

  1. (defroutes user-routes
  2.   (context "/user/:user-id" [user-id]
  3.     (GET "/profile" [] ...)
  4.     (GET "/posts" [] ...)))

因为 routes 是闭包,因此 user_id symbol 对于下面两个子 routes 都是可用的。
但是如果你内部的 routes 是在别处定义的,你就需要手工将 context 中的绑定参数传入进去。示例:

 

  1. (defn inner-routes [user-id]
  2.   (routes
  3.    (GET "/profile" [] ...)
  4.    (GET "/posts" [] ...)))

  5. (defroutes user-routes
  6.   (context "/user/:user-id" [user-id]
  7.     (inner-routes user-id)))

这是因为参数绑定是基于词法,而不是基于动态作用域的。

五、Response Rendering  -- 响应渲染


Compojure route 的响应会直接使用 compojure.response/render 方法进行处理。


六、常见问题


1、一个中间件试图读取 request 中的 :body 部分,但得到内容为空,怎么回事?

在 request 中, :body 的值是一个 InputStream。InputStream 是可变的(mutable)因此只能被读取一次。 在 InputStream 已被读取后,试图再次读取,操作都会失败。如果你发现 :body 值不是其实际值而是为空,那么多半是栈中更高一层的某个中间件已经读取过 InputStream 了。

Ex: 比如你在同一个栈中有两组 routes ,每一组都用到了某种 wrap-params 中间件。那么只有首先被调用的 routes 会获取到参数。
 
2、如何才能让 route 能够处理末尾为斜线的 URI ?

Compojure 还没法自动将 URI为斜线结尾的 requests 重定向到非斜线结尾的 routes handlers。变通的解决方案是使用一个中间件函数来去除 URI 末尾的斜线,或者调用一个 handlers 然后再重定向。

Ex: trailing-slash-middleware


阅读(1767) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~