插件服务器中的块抛出 MatchError 而不是使用 else 块

问题描述

以前,我会在 Elixir Plug 服务器中使用 with 块来解析请求中的参数,并在失败时返回一个正常的响应。但是,这似乎不再起作用(Elixir 1.11)。谁能指出这是怎么回事?

这是一个极简的 Plug 服务器,将显示问题

defmodule MatchTest.Router do
  use Plug.Router

  plug(:match)
  plug(:dispatch)

  get "/" do
    other_with =
      with {:ok,_} <- Map.fetch(%{},"test") do
        :ok
      else
        :error -> :error
      end

    with conn <- Plug.Conn.fetch_query_params(conn),{:ok,a} = Map.fetch(conn.query_params,"a") do
      Plug.Conn.send_resp(conn,200,"a = #{a}; other_with = #{other_with}")
    else
      :error -> Plug.Conn.send_resp(conn,400,"Incorrect Parameters")
    end
  end

  match _ do
    Plug.Conn.send_resp(conn,404,"Not Found")
  end
end

defmodule MatchTest do
  use Application

  def start(_type,_args) do
    Supervisor.start_link(
      [{Plug.Cowboy,scheme: :http,plug: MatchTest.Router,options: [port: 4000]}],strategy: :one_for_one,name: RateLimitedServer.Supervisor
    )
  end
end

正如预期的那样,当我在 GET 请求中包含 a 参数时,一切正常:

ddrexler@Drexbook-Pro:temp|$ http -v get localhost:4000/ a=="test"
GET /?a=test HTTP/1.1
Accept: */*
Accept-Encoding: gzip,deflate
Connection: keep-alive
Host: localhost:4000
User-Agent: HTTPie/2.3.0



HTTP/1.1 200 OK
cache-control: max-age=0,private,must-revalidate
content-length: 28
date: Mon,15 Feb 2021 22:40:14 GMT
server: Cowboy

a = test; other_with = error

特别是,第一个 with 子句按预期工作,当我们尝试从空映射中 Map.fetch() 时,它跳转else 子句(您可以看到这一点,因为字符串“other_with = error”)。

但是,当我尝试排除参数时,我得到了 500:

ddrexler@Drexbook-Pro:~|$ http -v get localhost:4000/
GET / HTTP/1.1
Accept: */*
Accept-Encoding: gzip,deflate
Connection: keep-alive
Host: localhost:4000
User-Agent: HTTPie/2.3.0



HTTP/1.1 500 Internal Server Error
content-length: 0

服务器得到一个未捕获的 MatchError:

ddrexler@Drexbook-Pro:match_test|$ mix run --no-halt
Compiling 1 file (.ex)
warning: "else" clauses will never match because all patterns in "with" will always match
  lib/match_test.ex:15


14:40:16.341 [error] #PID<0.350.0> running MatchTest.Router (connection #PID<0.349.0>,stream id 1) terminated
Server: localhost:4000 (http)
Request: GET /
** (exit) an exception was raised:
    ** (MatchError) no match of right hand side value: :error
        (match_test 0.1.0) lib/match_test.ex:16: anonymous fn/2 in MatchTest.Router.do_match/4
        (match_test 0.1.0) lib/plug/router.ex:284: MatchTest.Router.dispatch/2
        (match_test 0.1.0) lib/match_test.ex:1: MatchTest.Router.plug_builder_call/2
        (plug_cowboy 2.4.1) lib/plug/cowboy/handler.ex:12: Plug.Cowboy.Handler.init/2
        (cowboy 2.8.0) /Users/ddrexler/src/elixir/match_test/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.8.0) /Users/ddrexler/src/elixir/match_test/deps/cowboy/src/cowboy_stream_h.erl:300: :cowboy_stream_h.execute/3
        (cowboy 2.8.0) /Users/ddrexler/src/elixir/match_test/deps/cowboy/src/cowboy_stream_h.erl:291: :cowboy_stream_h.request_process/3
        (stdlib 3.13.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

还要注意警告 "else" clauses will never match because all patterns in "with" will always match - 显然是不真实的!存在它们不匹配的情况,因为我收到 MatchError。 else 块包含一个 :error 选项,它应该可以捕获此结果!

解决方法

您可能已经从@sbacarob 的评论中解决了这个问题。

您的错误即将到来,因为当没有 a 参数时:{:ok,a} = Map.fetch(conn.query_params,"a") 被评估为 {:ok,a} = :error 并引发 MatchError

为了避免 MatchError,您需要使用特殊的 with 特定运算符 <-。您可以将其视为一种“软匹配”运算符(其中 = 是“硬匹配”)。软匹配让匹配失败(在 with 中)并落入 else/end,而硬匹配将引发 MatchError

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...