跳转至内容

Compojure/核心库

来自维基教科书,开放世界中的开放书籍

关于最新信息的重要说明

[编辑 | 编辑源代码]

有关最新信息,请参阅官方 Compojure wiki。此页面严重过时,不应作为最近版本的指南。

compojure.http.*

[编辑 | 编辑源代码]

HTTP 库为 Compojure 提供了一种 RESTful 和函数式的方式来定义 Java servlet。其语法灵感来自 Ruby Web 框架 Sinatra。

要创建 servlet,您需要将一系列 HTTP 资源定义传递给 servlet 函数

(def #^{:doc "A simple greeter"} greet
  (servlet
    (GET "/greet" "Hello visitor!")
    (ANY "/*"     (page-not-found))))

Compojure 还提供了一个 defservlet 宏

(defservlet greet
  "A simple greeter"
  (GET "/greet" "Hello visitor!")
  (ANY "/*"     (page-not-found)))

传递给 defservlet 的资源定义是 Compojure 将 URL 路由与生成有用 HTTP 响应的代码关联起来的方式,例如网页或图像。

资源定义

[编辑 | 编辑源代码]

资源定义采用以下形式

(method route & body)

方法可以是任何标准 HTTP 方法之一

GET  POST  PUT  DELETE  HEAD

或者,如果您希望匹配任何 HTTP 方法,您可以使用

ANY

伪造 HTML 表单的 PUT 和 DELETE

[编辑 | 编辑源代码]

按照当前标准,HTML 表单只能调用 GET 和 POST 方法,但在 RESTful Web 服务中,您可能希望为资源实现 PUT 和 DELETE 方法。Compojure 提供了一种变通方法,使您能够为浏览器和限制较少的客户端实现 REST 的统一接口,而无需显式服务器端处理。客户端应发送一个名为“_method”的参数,其值为您真正想要调用的方法。您可以为此目的将隐藏字段添加到 HTML 表单中,其他客户端可以简单地调用 PUT 或 DELETE 而不带参数。Compojure 的处理程序基础结构将在调用处理程序之前将实际方法替换为“_method”参数的值。

有关使用此技术的示例,请参阅部分#Servlet 绑定

路由参数

[编辑 | 编辑源代码]

路由可以是固定字符串,例如“/greet”,但通常您希望将路由的某些部分分配给影响输出的参数

(GET "/greet/:name" {params :params} 
  (str "Hello " (params :name)))

在此,资源定义将“/greet”之后的路径分配给参数 :name。可以通过 route 函数访问来自路由的参数。

Servlet 绑定

[编辑 | 编辑源代码]

除了路由之外,在所有资源声明中默认情况下还提供了一些其他绑定

  • method - HTTP 方法
  • params - HTTP 参数的哈希映射
  • headers - HTTP 标头的哈希映射
  • cookies - HTTP cookie 的哈希映射
  • session - 对特定于会话的映射的 ref
  • request - HttpServletRequest 对象

以下是一个使用 session 和 params 绑定的示例

(GET "/name"
  (str "<p>Your current name is: " (@session :name) "</p>"
       "<form>Change name: <input name='name' type='text'>"
       "<input type='submit' value='Save'></form>"))
(POST "/name"
  (dosync
    (alter session assoc :name (params :name))
    (str "Your name was changed to " (@session :name))))

以下是一个使用“_method”变通方法将更新实现为 PUT 的相同示例

(GET "/name"
  (str "<p>Your current name is: " (@session :name) "</p>"
       "<form>Change name: <input name='name' type='text'>"
       "<input type='submit' value='Save'>"
       "<input type='hidden' name='_method' value='PUT'></form>"))
(PUT "/name"
  (dosync
    (alter session assoc :name (params :name))
    (str "Your name was changed to " (@session :name))))

生成响应

[编辑 | 编辑源代码]

可以通过响应对象修改响应,但这几乎没有必要。相反,Compojure 采用函数式方法,从资源的返回值构建 HTTP 响应。

在前面的示例中,您可以看到如何返回字符串会添加到响应主体中。其他标准 Clojure 类型会以不同的方式修改响应

  • java.lang.String - 添加到响应主体
  • java.lang.Number - 更改状态码
  • Clojure 映射 - 更新(合并)响应映射
  • Clojure 序列 - 懒加载添加到响应主体
  • java.io.File - 将文件流式传输到响应主体
  • java.io.InputStream - 从流中读取并添加到响应主体
  • java.net.URL - 将 URL 的资源流式传输到响应主体
  • java.servlet.http.Cookie - 将 cookie 添加到 HTTP 标头

可以使用标准 Clojure 向量将这些修改链接在一起

(GET "/text"
  [{:headers {"Content-Type" "text/plain"}}
   "This is plain text."
   "And some more text."])
(GET "/bad"
  [404 "<h1>This page does not exist!</h1>"])
(GET "/download"
  (file "public/compojure.tar.gz"))   ; 'file' is an alias to 'new java.io.File'

compojure.html

[编辑 | 编辑源代码]

HTML 库提供了一种通过向量树定义 HTML 或 XML 的方式。

(html [:p [:em "Hello World"]])
<p><em>Hello World</em></p>

标签名称取自向量的第一个项目,可以是字符串、符号或关键字。您还可以选择通过提供一个哈希映射作为向量的第二个项目来指定标签的属性

(html [:div {:class "footer"} "Page 1"])
<div class="footer">Page 1</div>

html 函数还为定义 id 和 class 属性提供了语法糖

(html [:h1.first  "Foo"]
      [:h2#second "Bar"])
<h1 class="first">Foo</h1><h2 id="second">Bar</h2>

任何序列都将扩展到包含向量中

(html [:em '("foo" "bar")])
<em>foobar</em>
(html [:ul
  (map (fn [x] [:li x])
       [1 2 3])])
<ul><li>1</li><li>2</li><li>3</li></ul>

compojure.server.jetty

[编辑 | 编辑源代码]

Jetty 库为 Jetty Web 服务器提供了一个 Clojure 友好的接口,因此您可以轻松地使用 servlet 映射创建 Web 服务器。

(def my-server
  (http-server {:port 8080}
    "/*"       my-main-servlet
    "/other/*" another-servlet
    ...))

您也可以使用 defserver 宏

(defserver my-server
  {:port 8080}
  "/*"       my-main-servlet
  "/other/*" another-servlet
  ...))

创建 Jetty 服务器后,使用 (start my-server) 和 (stop my-server) 启动和停止 Web 服务器。

华夏公益教科书