如何在phoenix控制器中读取小块数据,使用Plug.Conn
问题描述:
我的目标是能够在phoenix控制器内处理分块的HTTP请求。我认为解决方案是使用Plug.Conn.read_body
但是我收到错误或超时。如何在phoenix控制器中读取小块数据,使用Plug.Conn
目前我认为最好的解决方案是自定义解析器。
defmodule Plug.EventStreamParser do
@behaviour Plug.Parsers
alias Plug.Conn
def parse(conn, "text", "event-stream", _headers, _opts) do
Conn.read_body(conn, length: 2, read_length: 1, read_timeout: 1_000)
|> IO.inspect
{:ok, %{}, conn}
end
end
但是我总是在检查行得到{:error :timeout}
。
答
Plug.Conn.read_body/2
只读取请求主体的一个块。您需要递归调用它才能读取所有内容。你也不需要写一个解析器来阅读大块的正文(如果我正确理解你的问题,我不认为解析器甚至可以这样做)。如果请求的Content-Type
不是默认情况下Plug解析的请求,则可以从控制器调用Plug.Conn.read_body/2
。
这里有一个小的实现的递归从控制器调用Plug.Conn.read_body/2
:
defmodule MyApp.PageController do
use MyApp.Web, :controller
def index(conn, _params) do
{:ok, conn} = read_body(conn, [length: 1024, read_length: 1024], fn chunk ->
# We just print the size of each chunk we receive.
IO.inspect byte_size(chunk)
end)
text conn, "ok"
end
def read_body(conn, opts, f) do
case Plug.Conn.read_body(conn, opts) do
{:ok, binary, conn} ->
f.(binary)
{:ok, conn}
{:more, binary, conn} ->
f.(binary)
read_body(conn, opts, f)
{:error, term} ->
{:error, term}
end
end
end
这在大约1024字节的块读取主体(它不保证返回的二进制是完全一样大小的要求)。随着下面的请求登载有4000个字节:
$ head -c 4000 /dev/urandom | curl -XPOST http://localhost:4000 --data-binary @- -H 'Content-Type: application/vnd.me.raw'
ok
以下是记录到控制台:
[info] POST/
[debug] Processing by MyApp.PageController.index/2
Parameters: %{}
Pipelines: [:api]
1024
1024
1024
928
[info] Sent 200 in 3ms
你没读完的请求。你只是读1个字节。 – Aetherus
你想一次读多少数据?一次1个字节直到完成? – Dogbert
我们正在发送一个请求中的事件流,我想要读取每个SSE来单独处理 –