【问题标题】:Parsing Facebook's Signed Request in Clojure在 Clojure 中解析 Facebook 的签名请求
【发布时间】:2014-04-13 21:08:40
【问题描述】:

我正在尝试使用 Luminus 构建一个网站,以便学习一些 Clojure。我有多年的命令式经验,但现在才进入函数式编程。现在,我正在尝试process a signed_request object from Facebook

根据网站我必须:

  1. 在句点(“.”)上分割一个字符串,得到一个包含 2 个字符串的向量。
  2. 获取这些字符串中的第一个,使用 base64 对其进行解码并与密钥进行比较。
  3. 获取第二个字符串,使用 base64 对其进行解码,然后使用 JSON 再次解码。

如果我用命令式语言来做这件事,这真的很简单,但是当涉及到函数式方法时,我却一无所知。现在我只知道如何将字符串拆分为 2 个字符串的向量:

(defn parse-request [signed_request]
  ((clojure.string/split signed_request #"\.")
    ))

(defn redirect-page [signed_request]
  (layout/render "redirect.html"
                 {:parsed_request parse-request(signed_request)}))

(defroutes home-routes
  (GET "/" [] (home-page))
  (POST "/redirect" [signed_request] (redirect-page signed_request)))

redirect-page 在服务器接收到 POST 请求时运行,然后它接受 signed_request 并将其传递给 parse-request 函数。解决此问题的实用方法是什么?

【问题讨论】:

    标签: facebook clojure leiningen compojure ring


    【解决方案1】:

    我认为你的问题的基本答案是函数式编程更多的是关于输入和输出(想想一个数学函数),而命令式编程往往更多的是关于副作用和可变数据。与其思考“我需要做什么?”,不如思考“我的目标是哪种数据结构,我该如何定义它?”你也可以有副作用(比如打印),但通常你正在编写纯函数,它接受参数并返回一些东西。

    Destructuring 是 Clojure 中一个非常宝贵的工具,在这里它会派上用场。您想使用clojure.string/split 将一个字符串拆分为两个字符串,然后对其中一个字符串执行一些操作,然后对另一个执行其他操作。您可以使用let 绑定为每个字符串分配名称,如下所示:

    (let [[str1 str2] (clojure.string/split signed-request #"\.")]
      (do-stuff-with-str1-and-str2))
    

    我对这个特定问题不太熟悉,但根据您列出的 3 个步骤,听起来您将获得 2 个结果,每个字符串一个。因此,也许您应该专注于编写一个返回包含 2 个结果的向量的函数,如下所示:

    (defn process-signed-request [signed-request]
      (let [[str1 str2] (clojure.string/split signed-request #"\.")]
        [(compare-fn (decode-with-base-64 str1) secret)
         (decode-with-json (decode-with-base-64 str2))]))
    

    请注意,以上是部分伪代码——您需要将compare-fndecode-with-base-64decode-with-jsonsecret 替换为代表这些东西的实际代码——或者您可以将其保留为-is 并且只实现函数,以便 compare-fndecode-with-base-64decode-with-json 引用您编写的实际函数。这是函数式编程中的常用方法——编写一个简短的高级函数来定义问题的解决方案,然后返回并编写它使用的辅助函数。

    顺便说一句,您可以通过其他几种方式编写(decode-with-json (decode-with-base-64 str2)) 部分:

    1. ((comp decode-with-json decode-with-base-64) str2)
    2. (-> str2 decode-with-base-64 decode-with-json)

    我经常发现第二种方法,使用线程宏(->->>)在我知道我需要对对象执行的操作的顺序时很有帮助,并且我希望代码能够直观地阅读。我发现它更容易阅读,例如“获取str2,使用base 64 对其进行解码,然后使用JSON 再次对其进行解码。”

    另外,这只是一件很挑剔的事情,但是在 Clojure 中编码时要注意括号的顺序。您现在拥有代码的方式,括号应如下所示:

    (defn parse-request [signed_request]
      (clojure.string/split signed_request #"\."))
    
    (defn redirect-page [signed_request]
      (layout/render "redirect.html"
                     {:parsed_request (parse-request signed_request)}))
    

    如果您有很多命令式经验,那么您可能已经根深蒂固地使用 fn(x) 语法,以至于您不小心输入了该语法,而不是 Clojure 使用的 (fn x) Lisp 语法。

    虽然我在吹毛求疵,但在 Clojure 中使用连字符而不是下划线来命名符号是惯用的。所以,我会将signed_request:parsed_request 重命名为signed-request:parsed-request

    希望有帮助!

    【讨论】:

    • 很好的答案,谢谢!看来这会比我想象的要花更多的时间,也许我应该在考试期间不要这样做,哈哈。
    • 没有问题 :) 如果您想深入了解函数式编程,请考虑阅读 Learn You a Haskell。这是一本关于 Haskell 而非 Clojure 的书,但它是对函数式编程的精彩介绍,从第 1 点开始。实际上我在学习 Clojure 之前先学了一点 Haskell,它使 FP 概念更容易理解。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-22
    • 2012-09-07
    相关资源
    最近更新 更多