每过一段时间总会燃起一种用Common Lisp(下文简称CL)来写Web应用的冲动,继而就会开始感慨在CL的生态圈中居然没有一款好用的Web框架。尽管放狗搜索“common lisp web framework”可以找到一些——例如Caveman2,以及在Cliki中记录的一些其它框架。然后使用过其中一部分的人就会知道,大部分用起来的体验都不咋地。

在业界摸爬打滚了一小段时光(从业几年姑且可以这么说吧)后,感觉制作一款专门用于编写JSON-in-JSON-out的Web应用的Web框架应该是一个不错的点子——反正大家都是发出application/json的请求期望application/json的响应,于是乎就撸起袖子自己干了。不过完全从零开始编写起是不现实的,于是乎选择了一个“平台”来作为基础。这个平台就是Clack啦

Clack会负责屏蔽下层的Web Server的差异,它只需要我提供一个函数给它作为来访的HTTP请求的“handler”即可,然后在这个handler中我就可以为所欲为啦。Clack在收到HTTP请求后,会把HTTP请求中的一些信息组织为一个列表类型的值传递给这个handler。在这个handler中,我只需要综合运用CAR、CDR之类的奇怪名字的函数就可以拿到自己需要的东西了——当然了,鉴于这个列表是个plist,用CL提供的DESTRUCTURING-BIND就可以很方便地提取啦。

在这个plist中,就有一个叫做:RAW-BODY的p,它的值是一个“流”——是的,就是那种文件流的流!但它又不是一个路边随处可见的妖艳贱货的流,而是一个来自FLEXI-STREAMS这个包(指CL中的package)的流。FLEXI-STREAMS是一个提供流操作的库,鉴于我没有看过Gray streams相关的内容,就不在这里瞎逼逼误导读者了。总而言之,我必须找到一个办法可以从一个FLEXI-STREAMS提供的输入流类型的值中读出一些东西来。

其实这个办法很简单,就是用CL原生提供的读取流的函数即可——比如READ-SEQUENCE这样的函数。不过我得验证一下不是,为此,我需要有办法可以构造出一个FLEXI-STREAMS流类型的值出来。FLEXI-STREAMS提供了一个叫做MAKE-FLEXI-STREAM的函数,显然这个就是我所需要调用的最后一个函数了。从它的描述来看,它需要一个CL中的原生流来作为第一个参数才行。为此,我试了一下下面的代码

(let ((text "Hello, world!"))(with-input-from-string (s text)(let ((buffer (make-array (length text)))(fs (flexi-streams:make-flexi-stream s)))(read-sequence buffer fs))))

遗憾的是,运行上面的代码会报错

The value#\H
is not of type(UNSIGNED-BYTE 8)
when setting an element of (ARRAY (UNSIGNED-BYTE 8))[Condition of type TYPE-ERROR]Restarts:0: [RETRY] Retry SLIME interactive evaluation request.1: [*ABORT] Return to SLIME's top level.2: [ABORT] abort thread (#<THREAD "worker" RUNNING {1007766D53}>)Backtrace:0: ((SB-VM::OPTIMIZED-DATA-VECTOR-SET (UNSIGNED-BYTE 8)) #<unavailable argument> #<unavailable argument> #<unavailable argument>)1: (SB-IMPL:ANSI-STREAM-READ-SEQUENCE #(0 0 0 0 0 0 ...) #<SB-IMPL::STRING-INPUT-STREAM {1007975973}> 0 13)2: (READ-SEQUENCE #(0 0 0 0 0 0 ...) #<SB-IMPL::STRING-INPUT-STREAM {1007975973}> :START 0 :END 13)3: ((FLET FLEXI-STREAMS::FILL-BUFFER :IN FLEXI-STREAMS::READ-SEQUENCE*) 13)4: ((:METHOD FLEXI-STREAMS::READ-SEQUENCE* (FLEXI-STREAMS::FLEXI-LATIN-1-FORMAT T T T T)) #<FLEXI-STREAMS::FLEXI-LATIN-1-FORMAT (:ISO-8859-1 :EOL-STYLE :LF) {1007975B43}> #<FLEXI-STREAMS:FLEXI-INPUT-STRE..5: ((:METHOD STREAM-READ-SEQUENCE (TRIVIAL-GRAY-STREAMS:FUNDAMENTAL-INPUT-STREAM T)) #<FLEXI-STREAMS:FLEXI-INPUT-STREAM {1007975C73}> #(0 0 0 0 0 0 ...) 0 NIL) [fast-method]6: (READ-SEQUENCE #(0 0 0 0 0 0 ...) #<FLEXI-STREAMS:FLEXI-INPUT-STREAM {1007975C73}> :START 0 :END NIL)7: ((LAMBDA ()))8: (SB-INT:SIMPLE-EVAL-IN-LEXENV (LET ((TEXT "Hello, world!")) (WITH-INPUT-FROM-STRING (S TEXT) (LET # #))) #<NULL-LEXENV>)9: (EVAL (LET ((TEXT "Hello, world!")) (WITH-INPUT-FROM-STRING (S TEXT) (LET # #))))10: ((LAMBDA NIL :IN SWANK:INTERACTIVE-EVAL))--more--

既然在调用READ-SEQUENCE的时候出状况了,不妨试试下面的代码

(let ((text "Hello, world!"))(with-input-from-string (s text)(let ((fs (flexi-streams:make-flexi-stream s)))(read-char fs))))

再次令人遗憾的,它会抛出另一个状况(CL中的condition啦)

#<SB-IMPL::STRING-INPUT-STREAM {10020A7243}> is not a binary input stream.[Condition of type SIMPLE-TYPE-ERROR]Restarts:0: [RETRY] Retry SLIME interactive evaluation request.1: [*ABORT] Return to SLIME's top level.2: [ABORT] abort thread (#<THREAD "worker" RUNNING {1001F6E813}>)Backtrace:0: (SB-KERNEL:ILL-BIN #<SB-IMPL::STRING-INPUT-STREAM {10020A7243}>)1: (READ-BYTE #<SB-IMPL::STRING-INPUT-STREAM {10020A7243}> NIL NIL)2: ((:METHOD FLEXI-STREAMS::READ-BYTE* (FLEXI-STREAMS:FLEXI-INPUT-STREAM)) #<FLEXI-STREAMS:FLEXI-INPUT-STREAM {10020A74C3}>) [fast-method]3: ((:METHOD FLEXI-STREAMS::OCTETS-TO-CHAR-CODE (FLEXI-STREAMS::FLEXI-LATIN-1-FORMAT T)) #<FLEXI-STREAMS::FLEXI-LATIN-1-FORMAT (:ISO-8859-1 :EOL-STYLE :LF) {10020A7393}> #<unavailable argument>) [fast-me..4: ((:METHOD STREAM-READ-CHAR (FLEXI-STREAMS:FLEXI-INPUT-STREAM)) #<FLEXI-STREAMS:FLEXI-INPUT-STREAM {10020A74C3}>) [fast-method]5: (READ-CHAR #<FLEXI-STREAMS:FLEXI-INPUT-STREAM {10020A74C3}> T NIL #<unused argument>)6: ((LAMBDA ()))7: (SB-INT:SIMPLE-EVAL-IN-LEXENV (LET ((TEXT "Hello, world!")) (WITH-INPUT-FROM-STRING (S TEXT) (LET # #))) #<NULL-LEXENV>)8: (EVAL (LET ((TEXT "Hello, world!")) (WITH-INPUT-FROM-STRING (S TEXT) (LET # #))))9: ((LAMBDA NIL :IN SWANK:INTERACTIVE-EVAL))--more--

看来从一开始提供给MAKE-FLEXI-STREAM函数的参数就应当是一个“二进制”的流才对。为此,我需要借助FLEXI-STREAMS自身的力量——调用它的STRING-TO-OCTETS函数。使用这个函数,可以将一个字符串转换为某种编码下的字节数组,例如下面的代码

(flexi-streams:string-to-octets "Hello") ;#(72 101 108 108 111)

得到一串“octet”后,还需要将其转换为“流”才行。再次借助FLEXI-STREAMS的力量,调用它的MAKE-IN-MEMORY-INPUT-STREAM函数,然后将这个函数调用的返回值作为MAKE-FLEXI-STREAM的第一个参数即可,最终的代码如下

(let* ((text "Hello, world!")           ; 原始文本(octets (flexi-streams:string-to-octets text)) ; 使用flexi-streams转换为字节数组,因为下一个函数只接受这种类型的参数(memory-input (flexi-streams:make-in-memory-input-stream octets)) ; 同样先转换为内存中的流,因为下一个函数只接受这种类型的参数(flexi-stream (flexi-streams:make-flexi-stream memory-input)) ; 终于可以得到一个真正的flexi-stream了(buffer (make-array (flexi-streams:octet-length text)))) ; 这里其实用字节的长度还是用字符的长度(flexi-streams:char-length)都没差(read-sequence buffer flexi-stream)   ; 可以像处理CL中的流那样处理flexi-stream(print (coerce buffer 'string)))      ; 把字节数组拼成字符串再输出比较好看

全文完

flexi-streams用法简介相关推荐

  1. MSSQL Sql加密函数 hashbytes 用法简介

    原文:MSSQL Sql加密函数 hashbytes 用法简介 转自:http://www.maomao365.com/?p=4732 一.mssql sql hashbytes 函数简介 hashb ...

  2. Postman用法简介-Http请求模拟工具

    Postman用法简介-Http请求模拟工具 在我们平时开发中,特别是需要与接口打交道时,无论是写接口还是用接口,拿到接口后肯定都得提前测试一下,这样的话就非常需要有一个比较给力的Http请求模拟工具 ...

  3. InputStreamReader 和 OutputStreamWriter类用法简介,及演示。

    InputStreamReader 和 OutputStreamWriter类用法简介. 一.InputStreamReader类 InputStreamReader 将字节流转换为字符流.是字节流通 ...

  4. php中__FILE__常量用法简介

    php中__FILE__常量用法简介 http://blog.csdn.net/xbei07/article/details/5616020#comments出处 1.php中的__FILE__常量返 ...

  5. java test 用法,pytest基本用法简介

    1.安装pytest,打开dos窗口输入: pip install pytest 2.通过pycharm工具下载 3.创建pytest测试用例步骤 # 定义测试类 class testdivide: ...

  6. SAP Gateway与OData用法简介

    本文将分别从基础原理和实际应用两个角度介绍SAP Gateway与OData相关的知识点. 1 相关的事务代码(t-code) SEGW - SAP Gateway Service Builder / ...

  7. Matlab中 intlinprog函数用法简介

    Matlab中 intlinprog函数用法简介 本来想要自己亲手写一遍的,发现了一优质博文基本上跟我做过的例题大差不差,所以就直接放上链接. 参考链接 https://www.cnblogs.com ...

  8. intersect的用法简介

    intersect的用法简介 在Oracle中,"A minus B"用于将结果集B中含有的记录从结果集A中移除,即用于获取存在于结果集A中而不存在于结果集B中的记录:" ...

  9. Oracle SQL调优系列之no_unnest和unnest用法简介

    Oracle调优之no_unnest和unnest用法简介 本博客介绍Oracle SQL调优的一种常用也是很实用的方法,也即/*+no_unnest */和/*+ unnest*/,介绍Oracle ...

  10. 安装bwa软件linux,bwa软件用法简介

    本文全文摘抄于简书:bwa软件用法简介 仅用于方便个人学习,如有侵权,请提醒删除,谢谢 bwa 是一款将序列比对到参考基因组上的软件,包含了以下3种算法 BWA-backtrack BWA-SW BW ...

最新文章

  1. 使用runtime让button传递多个参数
  2. element 往node里面增加属性值_【Vue原理】Compile - 源码版 之 Parse 属性解析
  3. Android Intent 大全[转载]
  4. Akka之在IoT系统中使用Actor(一)
  5. 【报错】ValueError: not enough values to unpack expected 2, got1
  6. python邮件发送csv附件_Python2.7 smtplib发送带附件邮件报错STARTTLS解决方法
  7. android sdk版本兼容,Android 版本兼容
  8. php上传中文图片,用PHP处理图片文件的上传
  9. trackingmore快递查询平台_国际快递物流信息追踪查询
  10. 预览文章: 猿们平常都喜欢听啥音乐?
  11. BIOS、BootLoader、uboot对比
  12. Debian系、红帽系、Arch Linux系如何选择安装包
  13. 推挽输出和开漏输出详解
  14. argis利用gp工具打包tpk切片包
  15. Oracle存储过程打印输出错误信息、行号,快速排查
  16. 电视剧《奋斗》精彩对白节选---(九)
  17. linux 计算程序运行时间
  18. 用皮尔逊相关系数检查特征间的线性相关关系
  19. SpringBoot+Vue实现前后端分离的宠物医院管理系统
  20. python爬取58同城租房信息_python爬虫:找房助手V1.0-爬取58同城租房信息(示例代码)...

热门文章

  1. [学习笔记]Segment Tree Beats!九老师线段树
  2. Compass(更新中。。。)
  3. 小白学python系列-(3)基础数量类型
  4. 2.2. php://stdin php://stdout
  5. 运营商认为虚拟化也难快速降低企业OPEX
  6. 2016已经过去,2017即将开始
  7. 【Python 第2课】print
  8. 建立普通用户信任关系,
  9. 程序员总结:帮助你早些明白一些道理
  10. Delphi常用时间函数列表