在上一个章节,咱们已经构建了一个简单的应用程序。这让咱们体验到了温馨的开发环境以及提供给了咱们一个快速领略项目结构的机会。可是如今咱们须要慢下脚步来花些时间来理解一下组件运行的细节。html
因为Clojure社区崇尚的是简单、灵活的价值观,因此事情的解决每每会没有一个固定的解决规范。在Web堆栈这一块,几乎全部的组件都会有它的替代品。你能够挑选一个最适合你的来开发应用。在这本书中,咱们将着重于 Ring/Compojure 来进行构建真实可用的应用。web
在上一章节已经介绍了一个简单的应用:支持用户留言,而且能够查看到全部用户的留言。咱们围绕着项目目录中的文件,来分析了他们各自所要达成的目的。然而,咱们并无很是仔细的去分析文件中的代码是什么。而在本章中,你将学习一些必要的背景知识,以致于充分理解咱们的第一个应用。服务器
因为Clojure Web 堆栈是创建在JAVA HTTP Servlet API的基础上的,应用程序能够部署在任何的Servlet容器之上,好比Jetty、GlassFish或者是Tomcat。cookie
Clojure 的应用程序能够独立进行运行,也能够和已经存在的JAVA 应用部署在一个服务器上。(Clojure applications can be run standalone or can be deployed side-by-side with existing Java applications using an application server. 这句求更好的说法~我以为翻译可能有误差。)session
还因为不少的云服务器运行的是JAVA虚拟机,你也能够将你的应用部署到像 Amazon Web Services, Google App Engine, Heroku 以及 Jelastic 这样的平台上面。闭包
一个 servlet 接收到一个请求而后生成一个符合HTTP协议的相应的反馈。
这个API实现了一系列web应用须要的核心的方法:诸如cookies,sessions,还有像URL 重写这样的功能。然而,servlets是一个专门为JAVA进行设计的,而直接在Clojure下使用并无提供最好的体验。app
也不像其它的许多平台——好比Rails或者是Django这些,Clojure并无一个总体的框架。与此相对的,你能够一块儿使用好几个库来构建一个应用,咱们将集中介绍几个经常使用的进行web开发的库。框架
Ring的目标是将HTTP的细节进行抽象,以成为一个简单的模块化的API,用于构建大规模的应用。若是你使用Ruby或者Python进行web开发,那么它对应的就应该像是WSGI或者Rack这样的库。ide
因为Ring已经成为了构建Web应用的事实上的标准,围绕着它已经开发出了不少的工具和中间键。虽然在大多数状况下你并不会去直接使用Ring,但它会提高你对于设计的理解,也能帮助你更好的排查出本身应用中的故障。模块化
Ring包含了四个基础的组件:处理器、请求、应答以及中间件(the handler, the request, the response, and the middleware),让咱们一个部分一部分的看过来。
Ring使用了Clojure Map来表示客户端的请求以及服务器端返回的相应内容。控制器的功用就是处理进入的请求。它接收一个请求Map而后返回一个响应Map。一个简单的Ring控制器能够是这个样子的:
(def handler [request-map] {:status 200 :headers {"Content-Type" "text/html"} :body (str "<html><body> your IP is: " (:remote-addr request-map) "</body></html>")})
你能够清楚的看到,这里接收了一个至关于HTTP请求的请求Map,而后返回了一个至关于HTTP响应的Map。而后Ring就会使用这个Map去生成一个HTTP Servlet的请求以及相应的对象。(Ring then takes care of generating an HTTP servlet request, and response objects from these maps.)
前面的处理程序主要的功能就是返回一个HTML字符串——其中带有IP地址,而且设置相应状态为200。做为一个常见的操做,Ring专门提供了一个API来辅助生成这样的应答:
(defn handler [request-map] (response (str "<html><body> your IP is: " (:remote-addr request-map) "</body></html>")))
若是你想要建立一个自定义的响应,你只须要写一个能够接收请求Map,而且返回一个你自定义的响应的函数就能够了。接下来让咱们看看请求和响应的Map格式是什么样的。
请求响应的Map通常都包含服务器端口、URI、服务器地址和内容类型,还要加上主体部分(body)。在这个Map中出现的关键词都是和servlet API 以及 官方 HTTP RFC 进行对应的。
请求定义了下面的一些键。要注意的是下面像 :ssl-client-cert
这样的键并非每一个请求都必要的。
:http
或者 :https
:get
:head
:options
:put
:post
:delete
:context
可用的时候,会添加到它前面。除了这些Ring已经定义了的键以外,还可使用中间件函数来扩展请求Map来添加一些特殊的键值。在这个章节的后面咱们会演示怎么去作。
在响应的Map中只包含了3个键值用来描述HTTP响应:
状态码是一个数字,它在HTTP RFC中有定义,最小的有效数值是100。
头是一个包括了HTTP头键值对的Map。头能够是字符串也能够是一组字符串组成,在这种状况下,一个键对应其中的一个字符串。
最后,响应的主体部分是一个字符串,它会原样返回到客户端上。若是它是一个序列,那么它就会将每个元素的字符串发送到客户端。若是该响应是一个文件或者是输入流,那么服务器会将内容发送到客户端。
中间件容许将控制器(the handlers)包在函数中,以这样的方式来修改处理的请求。中间件还常常用来扩展基础的Ring控制器来适应你本身的应用。
中间件处理程序是一个接受已经存在的处理程序以及一些可选参数,返回一个新的添加了一些行为的处理程序。下面有一个例子:
(defn handler [request] (response (str "<html><body> your IP is: " (:remote-addr request) "</body></html>"))) (defn wrap-nocache [handler] (fn [request] (let [response (handler request)] (assoc-in response [:headers "Pragma"] "no-cache")))) (def app (wrap-nocache handler))
在这个例子中,封装函数接收一个处理函数,而且返回一个新的处理程序来做为处理程序。因为返回的函数定义在当前的包装函数之中,它能够直接在里面引用处理函数。当使用的时候,它将处理请求而且在响应Map中添加Pragma: no-cache
。
该包装函数被称之为「闭包」,由于它包装了操做函数以及参数,而且容许直接使用返回的函数。
刚刚使用的技术可让咱们建立出一些简单的方法,每个均可以解决一个特定的问题。咱们能够将他们方便的连接起来来实现复杂的逻辑。
适配器处于底层的HTTP协议以及处理程序之间,他们提供着必要的配置,诸如端口映射,以及处理HTTP请求的解析成为请求Map,将响应Map构建成为HTTP的响应。你一般不须要直接经过适配器进行交互。咱们不会再继续展开了。
年后第一个星期事情就是满满的啊!简直出乎意料,昨天休息了一天~只有今天有时间来继续完成没有完成的坑了~
翻译的可能仍是一如既往的烂,可是我会找机会从新润色一下的。 相信本身并不会放弃,继续持续的更新下去的。