开发web应用当然少不了web服务器了,咱们这里使用是的ring这个库。

零、添加依赖

打开根目录下的project.clj,找到:dependencies

1
:dependencies [[org.clojure/clojure "1.8.0"]]

有可能你看到的clojure版本跟我的不太一样,在里添加(ring)[https://github.com/ring-clojure/ring],添加后像下面这样

1
2
:dependencies [[org.clojure/clojure "1.8.0"]
               [ring "1.6.3"]]

然后在命令行运行:

1
lein deps

lein底层使用的是maven和(clojars)[https://clojars.org/],下载下来的依赖包会在你的用户目录下的.m2目录,下载的所有文件可以在~/.m2/repository/ring/ring/1.6.3/找到。如果你身在"赵国",那么你家里应该常备"楼梯"。

一、启动HTTP Server

打开src/learnweb/core.clj,这个是在project.clj里面的:main定义的入口点:

1
2
3
4
5
6
7
(ns learnweb.core
  (:gen-class))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

下面来改造一下core.clj文件,把需要的函数包含进代码,然后把http server跑起来,最终的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
(ns learnweb.core
  (:require [ring.adapter.jetty :refer [run-jetty]])  ;(1)
  (:gen-class))

(defn handler [request]
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body "Hello World!"}) ;(2)

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (run-jetty handler {:port 3000})) ;(3)

改造了三个地方(1), (2), (3)

(1) 添加运行http server的函数run-jettyrun-jetty函数需要另外一个函数来处理http请求,同时会把请求的参数传递给那个函数;

(2) 处理http的函数,这里简单的返回状态码200,网页的内容类型text/html,网页的内容"Hello World!"

(3) 通过run-jetty函数运行http server,启动的时候指定端口为3000

然后在命令行运行如下命令:

1
lein run

你将会看到如下的输出:

1
2
3
4
2016-03-08 21:47:49.003:INFO::main: Logging initialized @1130ms
2016-03-08 21:47:49.073:INFO:oejs.Server:main: jetty-9.2.10.v20150310
2016-03-08 21:47:49.113:INFO:oejs.ServerConnector:main: Started ServerConnector@29a5f4e7{HTTP/1.1}{0.0.0.0:3000}
2016-03-08 21:47:49.114:INFO:oejs.Server:main: Started @1241ms

打开浏览器,访问http://localhost:3000,你就会看到一个网页版的Hello World!

完整代码在我的github上面:Learn Clojure web

1
2
git clone https://github.com/kevindragon/learn-clojure-web
git checkout 1.2

工作原理

run-jetty函数在指定的端口启动一个web server,接收http request,然后把请求交给handler函数进行处理,上面的handler函数返回一个状态码200,响应头指定Content-Type为html内容,响应的内容为Hello World!

二、显示更多信息

显示浏览器的User agent

"Hello World!"改成

1
(str "User agent: " (get-in request [:headers "user-agent"]))

handler函数变成如下:

1
2
3
4
(defn handler [request]
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body (str "User agent: " (get-in request [:headers "user-agent"]))})

然后在命令行按ctrl-c终止上次的运行,再次执行lein run,刷新浏览器便可以看到你自己浏览器的user agent,我的浏览器显示为:

1
User agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36

你的浏览器显示可能会不一样。handler函数的参数为http request的内容,包括请求头信息、url、请求参数等等。这里面直接从request中取出user-agent的信息。

根据url上面的参数显示不同的内容

接下来让咱们实现网页的内容显示url上面的信息。

比如http://localhost:3000/hello/Kevin,网页显示为你好Kevin

如果是http://localhost:3000/hello/Kitty则显示为你好Kitty

如果是http://localhost:3000显示为Index page,其他任何情况都显示Page not found

完整代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(ns learnweb.core
  (:require [ring.adapter.jetty :refer [run-jetty]]
            [clojure.string :refer [split]])
  (:gen-class))

(defn uri-split [str]
  (filter #(not-empty %) (split str #"/")))    ; (1)

(defn route [request]
  (let [uri (:uri request)
        seg (uri-split uri)]
    (cond
      (= uri "/") "Index page"
      (= (first seg) "hello") (str "你好" (nth seg 1 "Oops"))
      :else "Page not found")))    ; (2)

(defn handler [request]
  (let [uri (:uri request)
        seg (uri-split uri)]
    {:status 200
     :headers {"Content-Type" "text/html; charset=utf-8"}
     :body (route request)}))    ; (3)

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (run-jetty handler {:port 3000}))

(1) 添加uri-split函数,用于分割uri成列表。/hello/Kitty –> [“hello” “Kitty”]

(2) 添加路由处理函数route,也就是根据uri参数把请求分发到不同的处理函数,得到uri列表后,然后使用cond函数做分发处理

(3) 在返回的内容使用路由函数处理

完整的代码请checkout到1.2分支。