我构建了一个简单的 CRUD 应用程序,可用于跟踪体重等健康指标。
current_user
。
这让我可以在需要时随时联系socket.assigns.current_user.id
。
但不幸的是,当我在“显示”模式中按“编辑”时,套接字不包含 current_user
。
此视图http://localhost:4000/weights/27/show/edit
这是来自日志:
[debug] HANDLE EVENT "save" in HealthTrackerWeb.WeightLive.Show
Component: HealthTrackerWeb.WeightLive.FormComponent
Parameters: %{"weight" => %{"weight" => "88888.1"}}
from show edit
socket:
#Phoenix.LiveView.Socket<
id: "phx-F36nLW9MP7w9NQZk",
endpoint: HealthTrackerWeb.Endpoint,
view: HealthTrackerWeb.WeightLive.Show,
parent_pid: nil,
root_pid: #PID<0.2019.0>,
router: HealthTrackerWeb.Router,
assigns: %{
__changed__: %{},
action: :edit,
flash: %{},
form: %Phoenix.HTML.Form{
source: #Ecto.Changeset<action: :validate, changes: %{}, errors: [],
data: #HealthTracker.HealthStats.Weight<>, valid?: true>,
impl: Phoenix.HTML.FormData.Ecto.Changeset,
id: "weight",
name: "weight",
data: %HealthTracker.HealthStats.Weight{
__meta__: #Ecto.Schema.Metadata<:loaded, "weights">,
id: 27,
weight: 88888.1,
user_id: 1,
inserted_at: ~N[2023-08-26 04:35:49],
updated_at: ~N[2023-08-26 04:35:58]
},
hidden: [id: 27],
params: %{"weight" => "88888.1"},
errors: [],
options: [method: "put"],
index: nil,
action: nil
},
id: 27,
myself: %Phoenix.LiveComponent.CID{cid: 1},
patch: "/weights/27",
title: "Edit Weight",
weight: %HealthTracker.HealthStats.Weight{
__meta__: #Ecto.Schema.Metadata<:loaded, "weights">,
id: 27,
weight: 88888.1,
user_id: 1,
inserted_at: ~N[2023-08-26 04:35:49],
updated_at: ~N[2023-08-26 04:35:58]
}
},
[error] GenServer #PID<0.2019.0> terminating
** (KeyError) key :current_user not found in: %{__changed__: %{}, action: :edit, flash: %{}, form: %Phoenix.HTML.Form{source: #Ecto.Changeset<action: :validate, changes: %{}, errors: [], data: #HealthTracker.Health
Stats.Weight<>, valid?: true>, impl: Phoenix.HTML.FormData.Ecto.Changeset, id: "weight", name: "weight", data: %HealthTracker.HealthStats.Weight{__meta__: #Ecto.Schema.Metadata<:loaded, "weights">, id: 27, weight:
88888.1, user_id: 1, inserted_at: ~N[2023-08-26 04:35:49], updated_at: ~N[2023-08-26 04:35:58]}, hidden: [id: 27], params: %{"weight" => "88888.1"}, errors: [], options: [method: "put"], index: nil, action: nil}, i
d: 27, myself: %Phoenix.LiveComponent.CID{cid: 1}, patch: "/weights/27", title: "Edit Weight", weight: %HealthTracker.HealthStats.Weight{__meta__: #Ecto.Schema.Metadata<:loaded, "weights">, id: 27, weight: 88888.1,
user_id: 1, inserted_at: ~N[2023-08-26 04:35:49], updated_at: ~N[2023-08-26 04:35:58]}}
(health_tracker 0.1.0) lib/health_tracker_web/live/weight_live/form_component.ex:57: HealthTrackerWeb.WeightLive.FormComponent.handle_event/3
(phoenix_live_view 0.19.5) lib/phoenix_live_view/channel.ex:630: anonymous fn/4 in Phoenix.LiveView.Channel.inner_component_handle_event/4
(telemetry 1.2.1) /Users/martins/Work/Elixir/health_tracker/deps/telemetry/src/telemetry.erl:321: :telemetry.span/3
(phoenix_live_view 0.19.5) lib/phoenix_live_view/diff.ex:204: Phoenix.LiveView.Diff.write_component/4
(phoenix_live_view 0.19.5) lib/phoenix_live_view/channel.ex:553: Phoenix.LiveView.Channel.component_handle_event/6
(stdlib 4.3) gen_server.erl:1123: :gen_server.try_dispatch/4
(stdlib 4.3) gen_server.erl:1200: :gen_server.handle_msg/6
(stdlib 4.3) proc_lib.erl:250: :proc_lib.wake_up/3
Last message: %Phoenix.Socket.Message{topic: "lv:phx-F36nLW9MP7w9NQZk", event: "event", payload: %{"cid" => 1, "event" => "save", "type" => "form", "value" => "weight%5Bweight%5D=88888.1"}, ref: "32", join_ref: "10
"}
[debug] MOUNT HealthTrackerWeb.WeightLive.Show
Parameters: %{"id" => "27"}
Session: %{"_csrf_token" => "BVgDB44u3_rdNtmnVSmgDpcq", "live_socket_id" => "users_sessions:J5U-KzfYHgC1dspOxhvXVQcGVtH4DjIl19_-Tz8NXMY=", "user_token" => <<39, 149, 62, 43, 55, 216, 30, 0, 181, 118, 202, 78, 198
, 27, 215, 85, 7, 6, 86, 209, 248, 14, 50, 37, 215, 223, 254, 79, 63, 13, 92, 198>>}
[debug] QUERY OK source="users_tokens" db=0.5ms idle=815.7ms
SELECT u1."id", u1."email", u1."hashed_password", u1."confirmed_at", u1."inserted_at", u1."updated_at" FROM "users_tokens" AS u0 INNER JOIN "users" AS u1 ON u1."id" = u0."user_id" WHERE ((u0."token" = $1) AND (u0."
context" = $2)) AND (u0."inserted_at" > $3::timestamp + (-(60)::numeric * interval '1 day')) [<<39, 149, 62, 43, 55, 216, 30, 0, 181, 118, 202, 78, 198, 27, 215, 85, 7, 6, 86, 209, 248, 14, 50, 37, 215, 223, 254, 7
9, 63, 13, 92, 198>>, "session", ~U[2023-08-26 05:35:40.069525Z]]
↳ Phoenix.LiveView.Utils.assign_new/3, at: lib/phoenix_live_view/utils.ex:79
ITS RUNNING
UserLiveAuth.on_mount/4: socket:
#Phoenix.LiveView.Socket<
id: "phx-F36nLW9MP7w9NQZk",
endpoint: HealthTrackerWeb.Endpoint,
view: HealthTrackerWeb.WeightLive.Show,
parent_pid: nil,
root_pid: #PID<0.2032.0>,
router: HealthTrackerWeb.Router,
assigns: %{
__changed__: %{current_user: true},
current_user: #HealthTracker.Accounts.User<
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: 1,
email: "[email protected]",
confirmed_at: nil,
inserted_at: ~N[2023-08-01 06:30:14],
updated_at: ~N[2023-08-01 06:30:14],
...
>,
flash: %{},
live_action: :edit
},
transport_pid: #PID<0.2013.0>,
...
我不明白为什么 current_user 被
on_mount()
分配给套接字,但当我从模式触发保存时不可用。
已分配 current_user 的套接字的 ID 与未分配 current_user 的套接字的 ID 相同。
已生成的
user_auth.ex
文件提供了一些帮助程序,因为 user_token
存储在会话内。
...
def on_mount(:mount_current_user, _params, session, socket) do
{:cont, mount_current_user(socket, session)}
end
def on_mount(:ensure_authenticated, _params, session, socket) do
socket = mount_current_user(socket, session)
if socket.assigns.current_user do
{:cont, socket}
else
socket =
socket
|> Phoenix.LiveView.put_flash(:error, "You must log in to access this page.")
|> Phoenix.LiveView.redirect(to: ~p"/users/log_in")
{:halt, socket}
end
end
...
defp mount_current_user(socket, session) do
Phoenix.Component.assign_new(socket, :current_user, fn ->
if user_token = session["user_token"] do
Accounts.get_user_by_session_token(user_token)
end
end)
end
...
使用这些助手的最佳方式就在里面
router.ex
。
...
scope "/", PicChatWeb do
pipe_through [:browser, :require_authenticated_user]
live_session :protected_messages,
on_mount: [
{PicChatWeb.UserAuth, :ensure_authenticated},
{PicChatWeb.UserAuth, :require_user_owns_message}
] do
live "/messages/new", MessageLive.Index, :new
live "/messages/:id/edit", MessageLive.Index, :edit
live "/messages/:id/show/edit", MessageLive.Show, :edit
end
end
scope "/", PicChatWeb do
pipe_through :browser
get "/", PageController, :home
live_session :messages, on_mount: [{PicChatWeb.UserAuth, :mount_current_user}] do
live "/messages", MessageLive.Index, :index
live "/messages/:id", MessageLive.Show, :show
end
end
...