Phoenix框架Bodyguard库conn变量报错排查及项目开发指导
Let’s break down the most likely causes of your conn-related error in the PostController and walk through what code you’ll need to add to get your Bodyguard setup working properly.
First: Diagnose the Conn Error
The most common culprit here is a mismatch in how you’re calling Bodyguard’s permit/4 function, or missing setup to make the current user available in the conn. Here’s what to check first:
You’re passing
conninstead of the current user toBodyguard.permit
Bodyguard’s corepermitfunction expects a user struct (not the conn itself) as the third argument. If you wrote something like:Bodyguard.permit(PostPolicy, :show, conn, post)That will throw an error because the policy’s
can?function is expecting a user, not aPlug.Connstruct. The correct call should useconn.assigns.current_user:with :ok <- Bodyguard.permit(PostPolicy, :show, conn.assigns.current_user, post) do # render the post endconn.assigns.current_userdoesn’t exist
If you haven’t set up authentication middleware (plugs) to populateconn.assigns.current_user, Bodyguard won’t have a user to check permissions against. Verify that your router’s post routes are using a pipe that includes an auth plug (like:require_authenticated_user), and that this plug correctly fetches the logged-in user from the session and adds it to the conn.Your
PostPolicydoesn’t implement theBodyguard.Policybehaviour correctly
Double-check that your policy module definescan?/3functions that match the right arguments (user, action, resource). For example, a basicPostPolicyshould look like this:defmodule Bg.PostPolicy do @behaviour Bodyguard.Policy # Allow any logged-in user to view the post list def can?(%Bg.Accounts.User{} = user, :index, _), do: true # Only allow owners to view/edit/delete their own posts def can?(%Bg.Accounts.User{} = user, action, %Bg.Blog.Post{} = post) when action in [:show, :update, :delete], do: user.id == post.user_id # Allow any logged-in user to create posts def can?(%Bg.Accounts.User{} = user, :create, _), do: true # Deny all other requests by default def can?(_, _, _), do: false endIf your
can?functions expect a conn instead of a user, that’ll also trigger errors.
Next: Code You Need to Add/Complete
Your project is missing a few key pieces to get Bodyguard fully functional:
Implement authentication plumbing
If you haven’t already, you’ll need:- A
Userschema (with fields likeemail,hashed_password) - Registration and session controllers to handle user sign-up/login
- Auth plugs to set
current_userin the conn (Phoenix’smix phx.gen.authgenerator can auto-create all this for you quickly) - Update your router to pipe authenticated plugs into post routes, e.g.:
scope "/", BgWeb do pipe_through [:browser, :require_authenticated_user] resources "/posts", PostController end
- A
Add unauthorized handling
Bodyguard lets you define how to handle denied requests. Add ahandle_unauthorized/1function to yourPostPolicy(or directly in the controller) to return a user-friendly response:# In PostPolicy def handle_unauthorized(conn) do conn |> Phoenix.Controller.put_flash(:error, "You don't have permission to do that!") |> Phoenix.Controller.redirect(to: Routes.post_path(conn, :index)) end # Then in your controller actions, handle the permit result: def show(conn, %{"id" => id}) do post = Bg.Blog.get_post!(id) case Bodyguard.permit(PostPolicy, :show, conn.assigns.current_user, post) do :ok -> render(conn, :show, post: post) {:error, :unauthorized} -> PostPolicy.handle_unauthorized(conn) end endValidate all controller actions
Make sure every action inPostController(index, create, update, delete) callsBodyguard.permitwith the correct user and resource. For example, thecreateaction would check if the user can create posts before saving:def create(conn, %{"post" => post_params}) do with :ok <- Bodyguard.permit(PostPolicy, :create, conn.assigns.current_user, nil), {:ok, post} <- Bg.Blog.create_post(post_params) do conn |> put_flash(:info, "Post created successfully.") |> redirect(to: Routes.post_path(conn, :show, post)) else {:error, :unauthorized} -> PostPolicy.handle_unauthorized(conn) {:error, %Ecto.Changeset{} = changeset} -> render(conn, :new, changeset: changeset) end end
Quick Debug Tip
If you’re still stuck, add IO.inspect(conn.assigns) at the start of your controller action to confirm whether current_user is present. If it’s not, your auth setup is the first thing to fix.
内容的提问来源于stack exchange,提问作者Yash Dani

