【问题标题】:elixir converting a header string into a map长生不老药将标题字符串转换为地图
【发布时间】:2019-11-15 05:07:00
【问题描述】:

我有以下字符串,它是来自 http 请求的 WWW-Authenticate 的值:

"Digest realm=\"Web Service Realm via Digest Authentication\", qop=\"auth\", nonce=\"MTU3MjchuUVEIHEUnVNV==\""

我需要将其转换为地图,以便轻松引用领域和随机数的值。我有一些非常脆弱的工作代码,例如提取领域:

    headers
    |> String.split(",")
    |> List.first()
    |> String.split("=")
    |> List.last()
    |> String.replace("\"", "")

但是,这并不是很好,因为它依赖于领域是列表中的第一个。转换此数据的最佳方式是什么?

【问题讨论】:

    标签: elixir


    【解决方案1】:

    如果您有 :cowlib 作为依赖项(:phoenix 通过 :plug_cowboy => :cowboy => :cowlib 依赖它),它包括用于解析各种标头的函数,包括“WWW-Authenticate”。

    iex> www_authenticate = "Digest realm=\"Web Service Realm via Digest Authentication\", qop=\"auth\", nonce=\"MTU3MjchuUVEIHEUnVNV==\""
    "Digest realm=\"Web Service Realm via Digest Authentication\", qop=\"auth\", nonce=\"MTU3MjchuUVEIHEUnVNV==\""
    iex> [digest: params] = :cow_http_hd.parse_www_authenticate(www_authenticate)
    [
      digest: [
        {"realm", "Web Service Realm via Digest Authentication"},
        {"qop", "auth"},
        {"nonce", "MTU3MjchuUVEIHEUnVNV=="}
      ]
    ]
    iex> Map.new(params)
    %{
      "nonce" => "MTU3MjchuUVEIHEUnVNV==",
      "qop" => "auth",
      "realm" => "Web Service Realm via Digest Authentication"
    }
    

    【讨论】:

    • ? 如果还没有的话,我什至会添加这个依赖而不是重新发明轮子。
    • 谢谢,这正是我想要的。或许我可以一开始就将其作为默认设置。
    【解决方案2】:

    我会发布递归解决方案,仅用于教育目的。当然,这可能不应该在生产中使用。

    defmodule Splitter do
      def parse(input), do: do_parse(input, %{}, :none)
    
      defp do_parse("realm=\"" <> rest, acc, :none),
        do: do_parse(rest, acc, :realm)
      defp do_parse("qop=\"" <> rest, acc, :none),
        do: do_parse(rest, acc, :qop)
      defp do_parse("nonce=\"" <> rest, acc, :none),
        do: do_parse(rest, acc, :nonce)
      defp do_parse("\"" <> rest, acc, key),
        do: do_parse(rest, acc, :none)
      defp do_parse(<<c :: binary-size(1), rest :: binary>>, acc, :none),
        do: do_parse(rest, acc, :none)
      defp do_parse(<<c :: binary-size(1), rest :: binary>>, acc, key),
        do: do_parse(rest, Map.update(acc, key, c, & &1 <> c), key)
      defp do_parse("", acc, _), do: acc
    end
    
    Splitter.parse(input)
    #⇒ %{
    #   nonce: "jefsFENEFJWIfejkshfshfhu332bfesf==",
    #   qop: "auth",
    #   realm: "Web Something Realm via Digest Authentication"
    # }
    

    【讨论】:

      【解决方案3】:

      正则表达式来拯救?这不包括任何验证或错误处理,但它适用于您的示例案例。

      ~r/([^\s=]+)="([^"]*)"/
      |> Regex.scan(headers)
      |> Enum.map(fn [_, k, v] -> {k, v} end)
      |> Map.new()
      

      地图中的结果

      %{
        "nonce" => "jefsFENEFJWIfejkshfshfhu332bfesf==",
        "qop" => "auth",
        "realm" => "Web Something Realm via Digest Authentication"
      }
      

      【讨论】:

      • 嗨,我意识到测试用例甚至不是一个字符串,所以我不得不将你的正则表达式修改为~r/([^\s=]+)=\\"([^"]*)"/,但它在 Elixir 中似乎不匹配,但使用 RegExr .com 匹配。
      • 实际上,我只是通过排除额外的转义来完成这项工作的 ~r/([^\s=]+)=\"([^"]*)"/
      【解决方案4】:

      您可以以更通用的方式使用String.split()

      str = ~s(Digest realm="Web Something Realm via Digest Authentication", qop="auth", nonce="jefsFENEFJWIfejkshfshfhu332bfesf==")
      [type, rest] = String.split(str, " ", [parts: 2])
      key_vals = String.split(rest, ", ")
      
      for key_val <- key_vals, into: %{} do
        [key, val] = String.split(key_val, "=", [parts: 2]) 
        {key, String.trim(val, ~s{"})}  #return a tuple for each key/val in map
      end
      |> Map.put_new("type", type) 
      

      =>

      %{
        "nonce" => "jefsFENEFJWIfejkshfshfhu332bfesf==",
        "qop" => "auth",
        "realm" => "Web Something Realm via Digest Authentication",
        "type" => "Digest"
      }
      

      【讨论】:

        猜你喜欢
        • 2015-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-08
        • 2019-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多