如何使用 Phoenix 框架渲染 JSON 文件?
How to render a JSON file with the Phoenix framework?
我不想从数据库中获取数据,而是想使用手动编写的 JSON 文件作为数据。假设我的数据是这样的:
[
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob", "email": "bob@example" },
{ "id": 3, "name": "Charles", "email": "charles@example.com"}
]
它写在一个名为 MOCK_DATA.json
的文件中。当我访问 localhost:port/api/v1/users
url 时,我应该如何渲染这个文件? localhost:port/api/v1/users/1
url 显示 { "id": 1, "name": "Alice", "email": "alice@example.com" }
怎么样?
如果文件是静态的(在服务过程中不会更改 运行),那么您可以在控制器中编译应用程序时读取它。查看模块参数(您在模块中定义的那些,在函数之外)。这样我就被解析一次了。
如果此文件是动态的,那么您可能需要在每次调用 API 时读取它并解析它。不建议这样做,因为从磁盘 IO 读取文件会减慢速度。
在任何情况下,解析为 Map 的文件结果都可以传入视图并呈现,与数据库没有区别。
编辑:另一个建议是重新格式化您的 JSON(如果可能)并将 id
作为键,其余数据作为值。这样 id
的查找会非常快,就像数据库中的主键索引一样。
这是一个基本的工作示例...
第 1 步:创建凤凰应用程序
例如,exjson 用于 ExampleJson 或您喜欢的任何名称
mix phoenix.new exjson --no-ecto --no-brunch --no-html
第二步:设置路由器
将此范围添加到 web/router.ex 文件
scope "/api/v1", Exjson do
pipe_through :api
resources "/users", UserController
end
第 3 步:将模拟数据放在应用程序可访问的位置
priv/data/MOCK_DATA.json
第 4 步:设置用户控制器
将用户控制器视为具有许多操作(功能)
conn struct 从你的凤凰端点连同任何
参数
defmodule Exjson.UserController do
use Exjson.Web, :controller
# GET http://localhost:4000/api/v1/users/
def index(conn, _params) do
users = File.read!(file) |> Poison.decode!()
render conn, users: users
end
# GET http://localhost:4000/api/v1/users/1
def show(conn, params) do
users = File.read!(file) |> Poison.decode!()
render conn, user: users |> Enum.find(&(&1["id"] === String.to_integer(params["id"])))
end
defp file() do
Path.join(:code.priv_dir(:exjson), "data/MOCK_DATA.json")
end
end
第 5 步:设置用户视图
您还可以将用户视图视为具有以适当方式呈现从控制器接收的数据的函数。在这种情况下,您使用的是 json 数据,因此 phoenix 有一些内置函数可以帮助解决这个问题。
defmodule Exjson.UserView do
use Exjson.Web, :view
def render("index.json", %{users: users}) do
render_many(users, __MODULE__, "user.json")
end
def render("show.json", %{user: user}) do
render_one(user, __MODULE__, "user.json")
end
def render("user.json", %{user: user}) do
%{
id: user["id"],
name: user["name"],
email: user["email"]
}
end
end
给你一些真正的代码来开始,这是我能想到的最简单的事情:
defmodule MyApp.UserController do
@mock_data (
Application.app_dir(:my_app, "priv/mock_data/users.json")
|> File.read!
|> Poison.decode!
)
def index(conn, _params) do
conn
|> put_status(:ok)
|> json(@mock_data)
end
end
然后只需将您的假数据保存在项目中的 priv/mock_data/users.json
中。那里可能有错别字,但你已经明白了基本的想法......
由于您只是将 JSON 回显给 /users
端点,在这种情况下您不需要 Poison.decode/1
调用,它会执行不必要的 decoding/encoding JSON.
因此,调整 Patrick 的代码:
defmodule MyApp.UserController do
@mock_data_string (
Application.app_dir(:my_app, "priv/mock_data/users.json")
|> File.read!
)
def index(conn, _params) do
conn
|> put_resp_content_type("application/json")
|> send_resp(200, @mock_data_string)
end
end
但是,对于 /users/1
端点,最好使用 Poison.decode/1
提取值,如@stephen_m 的回答所示。
我不想从数据库中获取数据,而是想使用手动编写的 JSON 文件作为数据。假设我的数据是这样的:
[
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob", "email": "bob@example" },
{ "id": 3, "name": "Charles", "email": "charles@example.com"}
]
它写在一个名为 MOCK_DATA.json
的文件中。当我访问 localhost:port/api/v1/users
url 时,我应该如何渲染这个文件? localhost:port/api/v1/users/1
url 显示 { "id": 1, "name": "Alice", "email": "alice@example.com" }
怎么样?
如果文件是静态的(在服务过程中不会更改 运行),那么您可以在控制器中编译应用程序时读取它。查看模块参数(您在模块中定义的那些,在函数之外)。这样我就被解析一次了。
如果此文件是动态的,那么您可能需要在每次调用 API 时读取它并解析它。不建议这样做,因为从磁盘 IO 读取文件会减慢速度。
在任何情况下,解析为 Map 的文件结果都可以传入视图并呈现,与数据库没有区别。
编辑:另一个建议是重新格式化您的 JSON(如果可能)并将 id
作为键,其余数据作为值。这样 id
的查找会非常快,就像数据库中的主键索引一样。
这是一个基本的工作示例...
第 1 步:创建凤凰应用程序例如,exjson 用于 ExampleJson 或您喜欢的任何名称
mix phoenix.new exjson --no-ecto --no-brunch --no-html
第二步:设置路由器
将此范围添加到 web/router.ex 文件
scope "/api/v1", Exjson do
pipe_through :api
resources "/users", UserController
end
第 3 步:将模拟数据放在应用程序可访问的位置
priv/data/MOCK_DATA.json
第 4 步:设置用户控制器
将用户控制器视为具有许多操作(功能) conn struct 从你的凤凰端点连同任何 参数
defmodule Exjson.UserController do
use Exjson.Web, :controller
# GET http://localhost:4000/api/v1/users/
def index(conn, _params) do
users = File.read!(file) |> Poison.decode!()
render conn, users: users
end
# GET http://localhost:4000/api/v1/users/1
def show(conn, params) do
users = File.read!(file) |> Poison.decode!()
render conn, user: users |> Enum.find(&(&1["id"] === String.to_integer(params["id"])))
end
defp file() do
Path.join(:code.priv_dir(:exjson), "data/MOCK_DATA.json")
end
end
第 5 步:设置用户视图
您还可以将用户视图视为具有以适当方式呈现从控制器接收的数据的函数。在这种情况下,您使用的是 json 数据,因此 phoenix 有一些内置函数可以帮助解决这个问题。
defmodule Exjson.UserView do
use Exjson.Web, :view
def render("index.json", %{users: users}) do
render_many(users, __MODULE__, "user.json")
end
def render("show.json", %{user: user}) do
render_one(user, __MODULE__, "user.json")
end
def render("user.json", %{user: user}) do
%{
id: user["id"],
name: user["name"],
email: user["email"]
}
end
end
给你一些真正的代码来开始,这是我能想到的最简单的事情:
defmodule MyApp.UserController do
@mock_data (
Application.app_dir(:my_app, "priv/mock_data/users.json")
|> File.read!
|> Poison.decode!
)
def index(conn, _params) do
conn
|> put_status(:ok)
|> json(@mock_data)
end
end
然后只需将您的假数据保存在项目中的 priv/mock_data/users.json
中。那里可能有错别字,但你已经明白了基本的想法......
由于您只是将 JSON 回显给 /users
端点,在这种情况下您不需要 Poison.decode/1
调用,它会执行不必要的 decoding/encoding JSON.
因此,调整 Patrick 的代码:
defmodule MyApp.UserController do
@mock_data_string (
Application.app_dir(:my_app, "priv/mock_data/users.json")
|> File.read!
)
def index(conn, _params) do
conn
|> put_resp_content_type("application/json")
|> send_resp(200, @mock_data_string)
end
end
但是,对于 /users/1
端点,最好使用 Poison.decode/1
提取值,如@stephen_m 的回答所示。