准备将您的脚趾浸入功能性编程池中吗? 保罗·格雷厄姆(Paul Graham)关于Lisp的经典文章也许说服了您尝试一下。 也许您想学习一种新的做事方式。 或者,也许您想查看可以提取多少代码和数据。
理想情况下,您的Web应用程序需要在IBM Cloud服务器上运行所获得的性能和高可用性。 在本教程中,我将向您展示如何使用Node.js作为IBM Cloud Web应用程序的一部分运行Clojure程序。
一个开发环境,支持将Node.js应用程序上载到IBM Cloud,例如Eclipse。
在IBM Cloud中运行应用程序后,您可以快速添加Git存储库和持续交付管道以自动开发,测试和部署它。 要进行设置,请在您应用的“概述”页面上,选择添加Git Repo和管道 ,然后您就可以在浏览器中进行所有开发和部署。
在GitHub上获取代码
Clojure是Lisp的一种方言,可以在JVM或JavaScript中执行。 要从Node.js应用程序运行它,请使用clojurescript-nodejs包。
编辑packages.json文件,将clojurescript-nodejs添加到软件包列表中: "dependencies": { "express": "4.13.x", "cfenv": "1.0.x", "clojurescript-nodejs": "*" }, 创建Clojure环境: // Get a Clojure environment var cljs = require('clojurescript-nodejs'); 定义一个Clojure函数( 有关代码的详细说明,请参见下一部分): cljs.eval('(ns app (:require clojure.string)) ' + '(defn h2 [str] (clojure.string/join ["<h2>" str "</h2>"]))'); 使用Clojure代码响应URL请求( 有关此代码的详细说明,请参见下一部分: app.get("/trivial", function(req, res) { res.send(cljs.eval('(ns app) (h2 "Hello, Clojure")')); }); 运行该应用程序并查看结果。如果您已经了解Clojure,甚至是其他Lisp方言,那么您将对本材料很熟悉。
在上面的步骤3中 ,当您定义Clojure函数时, cljs.eval函数将接收字符串并将其评估为Clojure代码。 在这种情况下,字符串之间用撇号( ' )分隔,因此也可以使用引号( " )。
cljs.eval('下面的Clojure语句有两件事:
将我们的命名空间定义为app 。 这很重要,因为如果不将变量,函数等放在名称空间中,就无法定义它们。 导入clojure.string库。 该库包含字符串处理函数,这些函数在生成HTML时很有用。 (ns app (:require clojure.string))下面的Clojure语句是实际的函数定义。 因为这是我们定义的第一个函数,所以让我们详细介绍一下。 第一部分定义了一个名为h2的函数。
(defn h2下面的部分指定该函数将具有一个称为str参数。 请注意,该部分使用方括号( [ ] )代替了典型的方括号。 Clojure与传统的Lisp不同,它使用不同的括号类型来表示不同的内容。 方括号定义了一个向量,该向量用作列表(用正括号括起来的项目的集合),但是允许更快地访问中间元素并更快地在末尾插入项目。
[str]接下来,从库中调用函数的语法是<library name>/<function> 。 这部分从clojure.string调用join 。 它是一个接收向量并返回包含所有项目的字符串的函数。 在这种情况下,它将字符串括在h2标签中:
(clojure.string/join ["<h2>" str "</h2>"])整个函数定义是一个列表。 最后的圆括号结束了。
)在上面的步骤4中 , app.get调用内的Clojure代码更加简单。 它首先将自身标识为app名称空间的一部分,然后调用之前定义的h2函数。 cljs.eval函数返回最后一个表达式的结果,在本例中为h2调用。 然后,这就是res.send发送给用户的值。
res.send(cljs.eval('(ns app) (h2 "Hello, Clojure")'));对于更复杂的事情,将整个应用程序放在Clojure中更容易,而在JavaScript中只包含一个小存根。
下面是残留的app.js。 第一行是编辑器指令,其目的是避免出现错误:
/*eslint-env node*/接下来,创建一个Clojure环境:
// Get a Clojure environment var cljs = require('clojurescript-nodejs');除了评估字符串,还评估文件:
// Evaluate the application library cljs.evalfile("app.cljs");app.cljs的第一部分等效于模板随附的app.cljs 在这里,逐行解释。
这些行定义名称空间( my-ns )并导入我们需要的两个库: clojure.string和cljs.nodejs 。 第一个只是导入,而第二个则包含在带有:as node的向量中,以指定其余名称空间中的代码将包称为node 。
(ns my-ns (:require clojure.string [cljs.nodejs :as node] ) )Clojure注释以分号( ; )开头。
; Get the application environment此行显示了在Clojure中用于与基础JavaScript代码进行通信的两种机制。 js/语法用于访问JavaScript全局变量,例如require 。 该代码段(js/require "cfenv")等同于require("cfenv") 。 要在Clojure中调用对象的方法,语法为(.< method > < object > < parameters, as needed >) 。 片段(.getAppEnv (js/require "cfenv"))等效于require("cfenv").getAppEnv() 。
最后, def定义了一个全局变量。 它计算第二个参数的值,并将其分配给第一个参数。 总的来说,这行等效于var appEnv = require("cfenv").getAppEnv() :
(def appEnv (.getAppEnv (js/require "cfenv")))除了没有方法调用外,此行与appEnv行类似:
; Create an express application (def express (js/require "express"))此行显示了另一种访问JavaScript的方式: (js* < expression >)运行该JavaScript表达式并返回值。 这行等效于var app = require("express")() :
(def app (js* "require('express')()"))此行显示了另一种使用JavaScript的机制。 当您有一个对象时,语法(aget < object > < attribute >)获取该对象的属性。 结合(.< method > < object > < parameters, as needed >)语法,此整行表示app.use(express.static("public")),这是用于指定在其中提供静态文件的代码公共目录。
; Static files (.use app ((aget express "static") "public"))这是一个代码示例,它指定对/ trivial路径的GET请求的响应。 创建匿名函数的语法为( fn [< parameters >] (< expression >) ) 。 将其与JavaScript方法的调用方式结合起来,等效于app.get( "/ trivial ", f unction(req, res) {res.send( " h ello")}) :
; Respond to a request (.get app "/trivial" (fn [req res] (.send res "Hello")))这等效于启动应用程序以侦听JavaScript中appEnv中指定的端口的代码:
; Start listening (.listen app (aget appEnv "port") "0.0.0.0" (fn [] (.log js/console (aget appEnv "url")) ) )看到此应用程序的响应(非常无聊)
无论用户输入如何,都返回固定响应的Web应用程序不是很有用。 实际的Web应用程序必须处理用户输入。 此输入通常以三种形式出现:在路径中,在查询中或在HTTP请求的正文中。
查看所有三种形式的用户输入
在Express(Node.js HTTP服务器库)中,通过指定:< keyword >作为路径组件来表示路径参数。 然后在req.params提供结果。 以下是相关代码。
首先,创建一个带有返回表单页面链接的字符串。 这仅仅是装饰性的,以使用户易于返回。
; Go back to the form (def goBackForm "<hr /><a href=\"/form.html\">Go back</a>");这是接收/process-path/< whatever >所有GET请求的实际调用:
; Process a part of the path (.get app "/process-path/:param"请求URL时调用的函数。 它使用两个字符串的串联来调用res.send :参数值和返回表单页面的链接HTML(先前定义为goBackForm )。
(fn [req res] (.send res (clojure.string/join [请记住, (*js < expression >)将表达式评估为JavaScript,然后将值返回给Clojure。 这使得读取JavaScript变量(例如req.params.param变得容易。
(js* "req.params.param") goBackForm ]) ) ) )查询参数甚至更易于处理。 不需要用冒号指定参数名称; 它们已经由浏览器从HTML创建。
; Respond to GET requests with a query (.get app "/process-form" (fn [req res] (.send res (clojure.string/join [ (js* "req.query.get") goBackForm ]) ) ) )主体中的参数(由POST和PUT方法使用)需要更多处理。 首先,您需要将process-body添加到packages.json文件。 然后,您需要将其用作中间件,以便在对主体条目进行编码时对其进行处理。 在该步骤之后,实际参数的访问方式与其他方法一样。
; Parse the body (.post app "*"JavaScript表达式返回一个函数。 在JavaScript和Clojure中,函数均可用作返回值和函数参数。
(js* "require('body-parser').urlencoded({extended: true})") ) ; Respond to POST requests (.post app "/process-form" (fn [req res] (.send res (clojure.string/join [ (js* "req.body.post") goBackForm ]) ) ) )通常,您不在乎参数来自何处; 您只关心他们的价值观。 因为参数具有键和值, 所以要使用的适当数据结构是map 。
下面是执行此操作的代码示例。 第一部分是函数定义。 因为这是一个命名函数,可以从其他地方调用它,所以它是用defn而不是fn定义的。
; Given a request object, return a map with all the parameters (defn getParams [req]let调用定义了可以在表达式的末尾使用的多个局部变量 (大致来说,它们在功能编程中实际上称为符号)。 在这种情况下,它定义了三个: queryMap , bodyMap和paramMap 。
(let [ ; Define local variables这三个定义都使用js->clj函数将JavaScript对象转换为Clojure映射。
queryMap (js->clj (js* "req.query")) bodyMap (js->clj (js* "req.body")) paramMap (js->clj (js* "req.params")) ]merge功能将三个不同的映射合并到一个映射中(如果键相同,则后面的映射中的值会覆盖前面的映射中的值)。 该表达式是let的结果,因此也是整个函数的结果。
(merge queryMap bodyMap paramMap) ) ; end of let ) ; end of defn这实际上是对请求的响应:
; Respond requests with parameters from all over (.all app "/merge/:pathparam" (fn [req res]这是let的另一种用法。 来自命令式编程的背景,我发现在大多数处理过程中,通过定义然后使用的符号,可以更轻松地编写和理解函数。
(let [ ; local variables调用我们之前定义的函数以获取参数。
params (getParams req) ] (.send res (clojure.string/join [现在,我们要使用JSON.stringify将地图转换为JSON。 为此,请使用clj->js将地图转换回JavaScript对象:
(.stringify js/JSON (clj->js params)) goBackForm ]) ; end of clojure.string/join ) ; end of res.send ) ; end of let ) ; end of fn [req res] ) ; end of app.all用于Node.js和Express软件包的相同工具可以用于任何其他API或软件包。
要使用软件包,请使用(def <var name > (js/require "< package name >")) 。 要调用方法,请使用(.< method > < object > < parameters, if any >) 。 要使用JavaScript中的全局变量,请使用js/< variable > 。 如果所有其他方法均失败,请使用以下语法评估JavaScript表达式: (js* "< JavaScript expression >") 。本教程为您提供了用Clojure编写Web应用程序的基础知识。 下一步是学习如何实际使用功能编程功能来使您的应用程序更好。 关于这个主题有很多书籍。 我最喜欢的是迈克尔·福格斯(Michael Fogus)和克里斯·豪斯(Chris Houser)创作的《克洛瑞尔的欢乐》 。
翻译自: https://www.ibm.com/developerworks/cloud/library/cl-clojure-node-bluemix-app/index.html