03 配置 Clojurescript 支持环境,并完成登录功能

03 配置 Clojurescript 支持环境,并完成登录功能.png

配置 project.clj

添加相关依赖

文件: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 主运行时库
            [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 环境插件
      [lein-figwheel "0.5.17-SNAPSHOT"]]

  
  ;; Ring 插件不通过 main 函数启动,只需要指定一个入口 Handler
  :ring {:handler soul-talk.core/app}
  
  
  ;; 不使用插件的时候,程序仍然从 main 函数启动
  ;; 启用 ClojureScript 之后,要关闭预编译 AOT
  :main ^:skip-aot soul-talk.core
  
  
  ;; 指定源文件和资源文件路径
  :source-paths ["src"]
  :resource-paths ["resources"]

  ;; 为 figwheel 指定 CSS 路径
  :figwheel {:css-dirs ["resources/public/css"]}

  ;; 设置自动清理路径
  :clean-targets ^{:protect false} [
    :target-path 
      ;; 下面的路径根据 cljsbuild 配置查找
      [:cljsbuild :builds :dev :compiler :output-dir]
      [:cljsbuild :builds :dev :compiler :output-to]]


  ;; 设置 cljsbuild 编译器参数
  :cljsbuild {
    :builds {
      ;; 开发环境
      :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"
          ;; 不优化    
          :optimizations          :none
          ;; 源代码
          :source-map-timestamp   true  
          ;; 打印格式               
          :pretty-print           true}}}}      


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

配置中间件

默认的 default 中间件会启用“防止跨域攻击”中间件,先关闭他

文件:src/soul_talk/core.clj

;; 组合中间件
(def app
  (-> app-routes
      (wrap-nocache)
      (wrap-reload)
      (wrap-webjars) 
      
      ;; 常用中间件,关闭跨域攻击功能
      (wrap-defaults (assoc-in site-defaults [:security :anti-forgery] false))))

静态资源

创建 base.html 静态页面父模板

注意:要引入 js/main.js 自定义脚本

文件:resources/base.html

<!DOCTYPE html>

<html lang="zh-cmn-Hans">

<head>
    <!-- 必须的标签 -->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <title>使用 Clojure 建立个人网站</title>

    <!-- 样式表 -->
    {% style "/assets/bootstrap/css/bootstrap.min.css" %}
    {% style "/assets/font-awesome/css/all.css" %}
    <link rel="stylesheet" href="/css/site.css">

    <!-- 可扩展的样式表 -->
    {% block page-css %}
    {% endblock %}
</head>

<body>

<div class="container-fluid">
    <!-- 可扩展的区域 -->
    {% 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>


<!-- 可扩展脚本-->
{% block page-script %}

{% endblock %}
</body>
</html>

修改 index.html

注意:页面接收 Handler 传来的一个变量,用于显示登录状态,但不是真正的 Session

文件:resources/index.html

<!-- 扩展 base.html -->
{% extends "base.html" %} 


<!-- 扩展 content 部分的内容 -->
{% 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>
            
            <!-- 根据登录状态显示不同内容 -->
            <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 href="https://getbootstrap.com/">Bootstrap</a> by <a href="https://twitter.com/mdo">@mdo</a>.</p>
    <p>
        <a href="#">Back to top</a>
    </p>
</footer>


<!-- 这里扩展 content 结束 -->
{% endblock %}

创建 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">&copy: 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

添加登录相关处理器

修改 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 登录数据
(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 不是系统 session,而只是一个传给模板的变量,仅仅对渲染页面起作用,其他页面就看不到这个 session 了。如果要使用系统 session,必须在返回的键值对中加入 :session 键:

(-> (redirect "/") (assoc :session {:identity email}))

注意二:这里 Post 完毕后直接返回了 Index.html 的 HTML ,因此 URL 还是 http://localhost:3000/login ,这是不正确的做法,应该重定向到 /index.html

添加退出登录处理器

退出流程:清空 Session 信息,跳转到首页即可

(ns soul-talk.core
  (:require ......
    ;; 引入重定向函数
    [ring.util.response :refer [redirect]]))
    
    
;; 退出登录,清空 Session ,调整到首页
(defn handle-logout [request]
  (do
    (assoc request :session {})
    (redirect "/")))

注意:这里清除的同样也只是一个模板变量,而不是系统 session 。如果要清楚系统 session ,需要将返回键值对中的 :session 键设置为空:

(-> (redirect "/") (assoc :session {}))

配置路由

将登录和退出处理器添加到路由规则中

文件:src/soul_talk/core.clj

(ns soul-talk.core
  (:require ......
    ;; 引入相关函数
    [compojure.core :refer [routes GET defroutes POST]]))
    
    
(def app-routes
  (routes
    (GET "/" request (home-handle request))
    (GET "/about" [] (str "这是关于我的页面"))
    
    
    ;; 登录路由,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)))

启动程序

启动服务

lein ring server-headless

编译 ClojureScript

lein figwheel

设置 Git 忽略文件

因为 ClojureScript 会下载很多依赖文件,同时产生很多编译输出文件,包括最终的输出文件 main.js ,都设置在了 /resources/public/js 中。这些都是动态的,不需要 Git 跟踪。

另外 FigWheel 的日志文件 figwheel_server.log 也不需要发布,因此都可以放到忽略文件中

figwheel_server.log
/resources/public/js
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容