美文网首页
01 Clojure Web 程序基本架构

01 Clojure Web 程序基本架构

作者: 勤劳的悄悄 | 来源:发表于2019-02-18 21:01 被阅读14次

    流程图

    01 - Clojure Web 程序基本结构.png

    创建项目

    lein new soul-talk
    

    配置 Git

    初始化 Git 仓库

    git init
    

    设置 .gitignore 忽略项

    /target
    /classes
    /checkouts
    pom.xml
    pom.xml.asc
    *.jar
    *.class
    /.lein-*
    /.nrepl-port
    .hgignore
    .hg/
    figwheel_server.log
    /resources/public/js
    db.sqlite
    

    提交到 GitHub

    到 Github 创建仓库,然后

    git remote add origin https://github.com/myqiao/soul-talk.git
    git push -u origin master
    

    配置 project.clj

    project.clj 中修改以下内容

    添加各种依赖库

    :dependencies [
            ;; Clojure 主运行时库
            [org.clojure/clojure "1.9.0"]
    
            ;; Ring 库
            [ring "1.7.1"]
    
            ;; 基于 Ring 的 Response 简化工具库
            [metosin/ring-http-response "0.9.1"]
    
            ;; 常用中间件集合
            [ring/ring-defaults "0.3.2"]
    
            ;; 路由库
            [compojure "1.6.1"]]
    

    依赖版本管理插件

    使用 lein-ancient 插件。在配置文件 project.clj 中添加

      :profiles {
            :user {
                :dependencies []
                :plugins [[lein-ancient "0.6.15"]]}}
    

    运行命令 lein ancient upgrade :interactive ,可以将项目的依赖都升级到最新版本

    指定程序启动入口函数

    可以指定命名空间中的某个函数作为程序启动入口函数

    ;; 程序运行入口
    :main soul-talk.core/foo
    

    也可以只指定命名空间,则命名空间中的 -main 函数自动成为启动入口函数

    ;; 程序运行入口
    :main soul-talk.core
    

    配置中间件

    一个中间件也是一个普通的函数,但是他接收一个 Handler ,返回一个 Handler

    创建自定义中间件

    创建一个自定义的中间件 wrap-nocache,功能是在头信息中添加不缓存设置

    ;; 自定义中间件:加入不缓存头信息
    (defn wrap-nocache [handler]
      (fn [request]
        (-> request
            handler
            (assoc-in [:headers "Pragma"] "no-cache"))))
    

    引入 Ring 内置中间件

    添加一个 Ring 内置的中间件 wrap-reload,但是目前自动载入还不好使,必须等 Ring 插件配置好才好使

    (ns soul-talk.core
      (:require ......
                [ring.middleware.reload :refer [wrap-reload]])) ;; 添加
    

    引入默认常用中间件

    依赖:[ring/ring-defaults "0.3.2"]

    ring-defaults 库包含了四种中间件:

    • api-defaults
    • site-defaults
    • secure-api-defaults
    • secure-site-defaults

    相当于启用了会话、快闪、调试、头信息、文件上传等等一系列内置中间件

    (ns soul-talk.core
      (:require ......
        ;; 引入常所用中间件
        [ring.middleware.defaults :refer :all]))
    

    添加静态资源

    创建静态 index.html 模板页面

    resources 目录下新建 index.html 模板页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>index</title>
    </head>
    <body>
    这是一个主页;
    </body>
    </html>
    

    创建自定义 Handler

    一个 Handler 就是接收一个 Request ,返回一个 Response 。二者在 Clojure 中都以哈希表的形式出现。

    在 Handler 中读取 index.html 作为响应模板

    修改 home-handle 函数,直接读取 index.html 返回

    (ns soul-talk.core
      (:require ......
        [clojure.java.io :as io]))
    
    ;; 自定义 Handler,读取静态页面返回
    (defn home-handle [request]
      (io/resource "index.html"))
    

    注意:在 resources 目录 下的资源可以被程序读取,而且不需要加路径,但是不能被用户直接访问

    在 Handler 中可以使用简化 Response 库

    依赖:[metosin/ring-http-response "0.9.1"]

    由于 ring.util.response 的功能比较基础,需要自己写返回码、头信息等等,我们可以使用 ring-http-response 库来简化代码。

    下面的代码并不是项目中的代码,只是用来演示

    (ns soul-talk.core
      (:require ......
        [ring.util.http-response :as resp]))
    
    (defn home-handle [request]
      ;; 这里简化了代码
      (resp/ok (str "<html><body><body>your IP is"
                    (:remote-addr request) 
                    "</body></html>")))
    

    配置路由

    使用 Compojure 路由库

    依赖:[compojure "1.6.1"]

    ;; 引入路由函数
    (ns soul-talk.core
      (:require ......
            [compojure.core :refer [routes GET]]
            [compojure.route :as route]))
    

    定义路由规则

    定义路由规则。注意:最终的路由变量 app-route 其实也是一个 Handler

    ;; 创建路由规则,最终返回的是一个普通的 Handler
    (def app-routes
      (routes
        (GET "/" request (home-handle request))
        (GET "/about" [] (str "这是关于我的页面"))
        (route/not-found "<h1>Page not found</h1>")))
    
    

    创建服务启动入口 Handler

    将所有的 Handler 、中间件、路由,通过链式调用,合并成一个启动 Handler ,绑定到一个名字上。注意:路由总是作为链式调用的第一个参数

    (def app
      (-> app-routes  ;; 链式调用的第一个参数为路由 Handler
          wrap-nocache
          ;; 自动重载中间件
          wrap-reload
          ;; 常用中间件
          (wrap-defaults site-defaults)))
    

    添加服务启动代码

    从主函数启动

    在主函数中调用 jetty/run-jetty ,将服务启动入口 Handler 传给他即可

    (ns soul-talk.core
      (:require ......
            [ring.adapter.jetty :as jetty]))
            
    (defn -main []
      (jetty/run-jetty app {:port 3000 :join? false}))
    

    从 Ring 插件启动

    使用 lein-ring 插件,需要给插件指定服务启动入口 Handler。在 project.clj 中添加以下代码:

    :plugins [[lein-ring "0.12.4"]]
    
    ;; 插件不通过 main 函数启动,只需要指定一个入口 Handler
    :ring {:handler soul-talk.core/app}
    
    

    启动服务

    从主函数 -main 启动

    lein run
    

    从 Ring 插件启动

    使用插件启动后,自动载入中间件才好使

    lein ring server ;; 默认端口 3000
    lein ring server 4000 ;; 
    lein ring server-headless ;; 不会打开浏览器
    

    最终代码

    project.clj

    (defproject soul-talk "0.1.0-SNAPSHOT"
      :description "FIXME: write description"
      :url "http://example.com/FIXME"
      :license {:name "Eclipse Public License"
                :url "http://www.eclipse.org/legal/epl-v10.html"}
      :dependencies [[org.clojure/clojure "1.9.0"]
                     
                     ;; Ring 库
                     [ring "1.7.1"]
                     
                     ;; 基于 Ring 的 Response工具库
                     [metosin/ring-http-response "0.9.1"]
                     
                     ;; 常用中间件集合
                     [ring/ring-defaults "0.3.2"]
                     
                     ;; 路由库
                     [compojure "1.6.1"]]
      
      
      ;; 基于 Lein 的 Ring 插件
      :plugins [[lein-ring "0.12.4"]]
    
      ;; 插件不通过 main 函数启动,只需要指定一个入口 Handler
      :ring {:handler soul-talk.core/app}
      
      ;; 不使用插件的时候,程序仍然从 main 函数启动
      :main soul-talk.core
      
      
      :profiles {
            :user {
                :dependencies []
                :plugins [[lein-ancient "0.6.15"]]}})
    
    

    src/soul-takl/core.clj

    (ns soul-talk.core
      (:require
        ;; 标准库 io 函数
        [clojure.java.io :as io]
    
        ;; 响应简化库
        [ring.util.http-response :as resp]
    
        ;; 中间件
        [ring.middleware.reload :refer [wrap-reload]]
        [ring.middleware.defaults :refer :all]
    
        ;; 路由库
        [compojure.core :refer [routes GET]]
        [compojure.route :as route]
    
        ;; 服务启动函数
        [ring.adapter.jetty :as jetty])) 
    
    
    ;; ************************************************
    ;; Handler 区域
    ;; ************************************************
    
    ;; 自定义 Handler,读取静态页面返回
    (defn home-handle [request]
      (io/resource "index.html"))
    
    
    
    ;; ************************************************
    ;; 路由 区域
    ;; ************************************************
    
    ;; 创建路由规则,最终返回的是一个普通的 Handler
    (def app-routes
      (routes
        (GET "/" request (home-handle request))
        (GET "/about" [] (str "这是关于我的页面"))
        (route/not-found "<h1>Page not found</h1>")))
    
    
    ;; ************************************************
    ;; 中间件 区域
    ;; ************************************************
    
    ;; 自定义中间件:加入不缓存头信息
    (defn wrap-nocache [handler]
      (fn [request]
        (-> request
            handler
            (assoc-in [:headers "Pragma"] "no-cache"))))
    
    
    
    ;; ************************************************
    ;; 启动代码 区域
    ;; ************************************************
    
    (def app
      (-> app-routes  ;; 链式调用的第一个参数为路由 Handler
          wrap-nocache
          ;; 自动重载中间件
          wrap-reload
          ;; 常用中间件
          (wrap-defaults site-defaults)))
    
    
    (defn -main []
      (jetty/run-jetty app {:port 3000 :join? false}))
    

    resources/index.html

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>index</title>
    </head>
    <body>
    这是一个主页;
    </body>
    </html>
    

    相关文章

      网友评论

          本文标题:01 Clojure Web 程序基本架构

          本文链接:https://www.haomeiwen.com/subject/zsmzeqtx.html