配置 project.clj
添加相關(guān)依賴
文件:project.clj
;; ClojureScript 庫
[org.clojure/clojurescript "1.10.439"]
配置前端編譯器和 Figwheel
文件: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 [
;; Clojure 主運(yùn)行時庫
[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"]
;; Selmer 模板庫
[selmer "1.12.0"]
;; ring 前端庫中間件
[ring-webjars "0.2.0"]
;; JQuery 依賴,Bootstrap 需要
[org.webjars/jquery "3.3.1-1"]
;; Bootstrap
[org.webjars/bootstrap "4.0.0-2"]
;; Popper
[org.webjars/popper.js "1.14.1"]
;; 字體庫
[org.webjars/font-awesome "5.5.0"]
;; ClojureScript 庫
[org.clojure/clojurescript "1.10.439"]]
:plugins [
;; 基于 Lein 的 Ring 插件
[lein-ring "0.12.4"]
;; Cljsbuild 編譯器插件
[lein-cljsbuild "1.1.7" :excludes [[org.clojure/clojure]]]
;; figwheel 環(huán)境插件
[lein-figwheel "0.5.17-SNAPSHOT"]]
;; Ring 插件不通過 main 函數(shù)啟動挂疆,只需要指定一個入口 Handler
:ring {:handler soul-talk.core/app}
;; 不使用插件的時候州丹,程序仍然從 main 函數(shù)啟動
;; 啟用 ClojureScript 之后给梅,要關(guān)閉預(yù)編譯 AOT
:main ^:skip-aot soul-talk.core
;; 指定源文件和資源文件路徑
:source-paths ["src"]
:resource-paths ["resources"]
;; 為 figwheel 指定 CSS 路徑
:figwheel {:css-dirs ["resources/public/css"]}
;; 設(shè)置自動清理路徑
:clean-targets ^{:protect false} [
:target-path
;; 下面的路徑根據(jù) cljsbuild 配置查找
[:cljsbuild :builds :dev :compiler :output-dir]
[:cljsbuild :builds :dev :compiler :output-to]]
;; 設(shè)置 cljsbuild 編譯器參數(shù)
:cljsbuild {
:builds {
;; 開發(fā)環(huán)境
:dev {
;; 源代碼目錄
:source-paths ["src-cljs"]
;; 開啟 figwheel
:figwheel true
:compiler {
;; 主命名空間
:main soul-talk.core
;; 依賴文件路徑
:asset-path "js/out"
;; 最終輸出的文件
:output-to "resources/public/js/main.js"
;; 臨時文件輸出路徑
:output-dir "resources/public/js/out"
;; 不優(yōu)化
:optimizations :none
;; 源代碼
:source-map-timestamp true
;; 打印格式
:pretty-print true}}}}
:profiles {
:user {
:dependencies []
:plugins [[lein-ancient "0.6.15"]]}}
)
配置中間件
默認(rèn)的 default
中間件會啟用“防止跨域攻擊”中間件,先關(guān)閉他
文件:src/soul_talk/core.clj
;; 組合中間件
(def app
(-> app-routes
(wrap-nocache)
(wrap-reload)
(wrap-webjars)
;; 常用中間件珍昨,關(guān)閉跨域攻擊功能
(wrap-defaults (assoc-in site-defaults [:security :anti-forgery] false))))
靜態(tài)資源
創(chuàng)建 base.html
靜態(tài)頁面父模板
注意:要引入 js/main.js
自定義腳本
文件:resources/base.html
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<!-- 必須的標(biāo)簽 -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>使用 Clojure 建立個人網(wǎng)站</title>
<!-- 樣式表 -->
{% style "/assets/bootstrap/css/bootstrap.min.css" %}
{% style "/assets/font-awesome/css/all.css" %}
<link rel="stylesheet" href="/css/site.css">
<!-- 可擴(kuò)展的樣式表 -->
{% block page-css %}
{% endblock %}
</head>
<body>
<div class="container-fluid">
<!-- 可擴(kuò)展的區(qū)域 -->
{% block content %}
{% endblock %}
</div>
<!-- scripts -->
{% script "/assets/jquery/jquery.min.js" %}
{% script "/assets/font-awesome/js/all.js" %}
{% script "/assets/bootstrap/js/bootstrap.min.js" %}
<!-- 引入自定義腳本-->
<script src="/js/main.js" type="text/javascript"></script>
<!-- 可擴(kuò)展腳本-->
{% block page-script %}
{% endblock %}
</body>
</html>
修改 index.html
注意:頁面接收 Handler 傳來的一個變量县耽,用于顯示登錄狀態(tài)订咸,但不是真正的 Session
文件:resources/index.html
<!-- 擴(kuò)展 base.html -->
{% extends "base.html" %}
<!-- 擴(kuò)展 content 部分的內(nèi)容 -->
{% block content %}
<div class="container">
<header class="blog-header py-3">
<div class="row flex-nowrap justify-content-between align-items-center">
<div class="col-4 pt-1">
<a class="text-muted" href="#">訂閱</a>
</div>
<div class="col-4 text-center">
<a class="blog-header-logo text-dark" href="#">Soul Talk</a>
</div>
<!-- 根據(jù)登錄狀態(tài)顯示不同內(nèi)容 -->
<div class="col-4 d-flex justify-content-end align-items-center">
{% if session.identity %}
<span class="navbar-text">歡迎你 {{session.identity}} </span>
<a class="btn btn-sm btn-outline-secondary" href="/logout">退出</a>
{% else %}
<a class="btn btn-sm btn-outline-secondary" href="/login">登錄</a>
{% endif %}
</div>
</div>
</header>
<div class="nav-scroller py-1 mb-2">
<nav class="nav d-flex justify-content-between">
<a class="p-2 text-muted" href="#">World</a>
<a class="p-2 text-muted" href="#">U.S.</a>
<a class="p-2 text-muted" href="#">Technology</a>
<a class="p-2 text-muted" href="#">Design</a>
<a class="p-2 text-muted" href="#">Culture</a>
<a class="p-2 text-muted" href="#">Business</a>
<a class="p-2 text-muted" href="#">Politics</a>
<a class="p-2 text-muted" href="#">Opinion</a>
<a class="p-2 text-muted" href="#">Science</a>
<a class="p-2 text-muted" href="#">Health</a>
<a class="p-2 text-muted" href="#">Style</a>
<a class="p-2 text-muted" href="#">Travel</a>
</nav>
</div>
<div class="jumbotron p-3 p-md-5 text-white rounded bg-dark">
<div class="col-md-6 px-0">
<h1 class="display-4 font-italic">Title of a longer featured blog post</h1>
<p class="lead mb-0"><a href="#" class="text-white font-weight-bold">Continue reading...</a></p>
</div>
</div>
</div>
<main role="main" class="container">
<div class="row">
<div class="col-md-8 blog-main">
<h3 class="pb-3 mb-4 font-italic border-bottom">
From the Firehose
</h3>
<div class="blog-post">
<h2 class="blog-post-title">Sample blog post</h2>
<p class="blog-post-meta">January 1, 2014 by <a href="#">Mark</a></p>
<ol>
<li>Vestibulum id ligula porta felis euismod semper.</li>
<li>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</li>
<li>Maecenas sed diam eget risus varius blandit sit amet non magna.</li>
</ol>
<p>Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis.</p>
</div><!-- /.blog-post -->
<div class="blog-post">
<h2 class="blog-post-title">Another blog post</h2>
<p class="blog-post-meta">December 23, 2013 by <a href="#">Jacob</a></p>
<p>Cum sociis natoque penatibus et magnis <a href="#">dis parturient montes</a>, </p>
</div><!-- /.blog-post -->
<div class="blog-post">
<h2 class="blog-post-title">New feature</h2>
<p class="blog-post-meta">December 14, 2013 by <a href="#">Chris</a></p>
<p>Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue.</p>
</div><!-- /.blog-post -->
<nav class="blog-pagination">
<a class="btn btn-outline-primary" href="#">Older</a>
<a class="btn btn-outline-secondary disabled" href="#">Newer</a>
</nav>
</div><!-- /.blog-main -->
<aside class="col-md-4 blog-sidebar">
<div class="p-3 mb-3 bg-light rounded">
<h4 class="font-italic">About</h4>
<p class="mb-0">Etiam porta <em>sem malesuada magna</em> mollis euismod. </p>
</div>
<div class="p-3">
<h4 class="font-italic">Archives</h4>
<ol class="list-unstyled mb-0">
<li><a href="#">March 2014</a></li>
<li><a href="#">February 2014</a></li>
<li><a href="#">January 2014</a></li>
<li><a href="#">December 2013</a></li>
<li><a href="#">November 2013</a></li>
<li><a href="#">October 2013</a></li>
<li><a href="#">September 2013</a></li>
<li><a href="#">August 2013</a></li>
<li><a href="#">July 2013</a></li>
<li><a href="#">June 2013</a></li>
<li><a href="#">May 2013</a></li>
<li><a href="#">April 2013</a></li>
</ol>
</div>
<div class="p-3">
<h4 class="font-italic">Elsewhere</h4>
<ol class="list-unstyled">
<li><a href="#">GitHub</a></li>
<li><a href="#">Twitter</a></li>
<li><a href="#">Facebook</a></li>
</ol>
</div>
</aside><!-- /.blog-sidebar -->
</div><!-- /.row -->
</main><!-- /.container -->
<footer class="blog-footer">
<p>Blog template built for <a >Bootstrap</a> by <a >@mdo</a>.</p>
<p>
<a href="#">Back to top</a>
</p>
</footer>
<!-- 這里擴(kuò)展 content 結(jié)束 -->
{% endblock %}
創(chuàng)建 login.html
文件:resources/login.html
{% extends "base.html" %}
{% block page-title %}Soul Talk Login {% endblock %}
{% block page-css %}
<link rel="stylesheet" href="/css/login.css">
{% endblock %}
{% block content %}
<div class="container text-center">
<form class="form-signin" action="/login" method="post" id="loginForm">
<img src="" alt="" class="mb-4">
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
<label for="email" class="sr-only">Email Address</label>
<input type="text" id="email" name="email" class="form-control" placeholder="Email Address" required autofocus/>
<label for="password" class="sr-only">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required />
<div class="check-box mb-3">
<label>
<input type="checkbox" value="Remember me">記住我
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">登錄</button>
<p class="mt-5 mb-3 text-muted">©: 2018</p>
</form>
</div>
{% endblock %}
ClojureScript
新建 src-cljs
目錄,在這個目錄下新建 soul_talk/core.cljs
文件
(ns soul-talk.core)
(defn main []
(enable-console-print!)
(prn "Hello, Clojurescript"))
(main)
Clojure
添加登錄相關(guān)處理器
修改 src/soul_talk/core.clj
酬诀,添加處理登錄請求的處理器
;; 渲染 index.html 頁面
(defn home-handle [request]
(parser/render-file "index.html" request))
;; Get 登錄頁面
(defn login-page [request]
(parser/render-file "login.html" {}))
;; Post 登錄數(shù)據(jù)
(defn handle-login [email password request]
(if (and (= email "jiesoul@gmail.com") (= password "12345678"))
;; 如果登錄成功脏嚷,則在 Session 中添加信息
(home-handle (assoc-in request [:session :identity] email))
;; 如果失敗,則返回登陸頁面瞒御,并向頁面中傳送錯誤信息
(login-page (assoc request :error "用戶名密碼不對"))))
注意一:這里的 session 不是系統(tǒng) session父叙,而只是一個傳給模板的變量,僅僅對渲染頁面起作用肴裙,其他頁面就看不到這個 session 了趾唱。如果要使用系統(tǒng) session,必須在返回的鍵值對中加入
:session
鍵:
(-> (redirect "/") (assoc :session {:identity email}))
注意二:這里 Post 完畢后直接返回了
Index.html
的 HTML 蜻懦,因此 URL 還是http://localhost:3000/login
甜癞,這是不正確的做法,應(yīng)該重定向到/index.html
添加退出登錄處理器
退出流程:清空 Session 信息宛乃,跳轉(zhuǎn)到首頁即可
(ns soul-talk.core
(:require ......
;; 引入重定向函數(shù)
[ring.util.response :refer [redirect]]))
;; 退出登錄悠咱,清空 Session ,調(diào)整到首頁
(defn handle-logout [request]
(do
(assoc request :session {})
(redirect "/")))
注意:這里清除的同樣也只是一個模板變量征炼,而不是系統(tǒng) session 析既。如果要清楚系統(tǒng) session ,需要將返回鍵值對中的
:session
鍵設(shè)置為空:
(-> (redirect "/") (assoc :session {}))
配置路由
將登錄和退出處理器添加到路由規(guī)則中
文件:src/soul_talk/core.clj
(ns soul-talk.core
(:require ......
;; 引入相關(guān)函數(shù)
[compojure.core :refer [routes GET defroutes POST]]))
(def app-routes
(routes
(GET "/" request (home-handle request))
(GET "/about" [] (str "這是關(guān)于我的頁面"))
;; 登錄路由谆奥,Get 和 POST
(GET "/login" request (login-page request))
(POST "/login" [email password :as req] (handle-login email password req))
;; 退出登錄路由==========
(GET "/logout" request (handle-logout request))
(route/not-found error-page)))
啟動程序
啟動服務(wù)
lein ring server-headless
編譯 ClojureScript
lein figwheel
設(shè)置 Git 忽略文件
因?yàn)?ClojureScript 會下載很多依賴文件眼坏,同時產(chǎn)生很多編譯輸出文件,包括最終的輸出文件 main.js 酸些,都設(shè)置在了 /resources/public/js 中宰译。這些都是動態(tài)的,不需要 Git 跟蹤魄懂。
另外 FigWheel 的日志文件 figwheel_server.log 也不需要發(fā)布沿侈,因此都可以放到忽略文件中
figwheel_server.log
/resources/public/js