我正在尝试开发基于角色的访问控制Pheonix API。用户将在其中登录并创建会话。登录后,他可以创建一个帖子,创建的帖子将分配给该user_id。我遇到了这个问题,user_id像字符串而不是里面的值一样出现。
我已经尝试过String.to_integer(user_id),但是它给出了错误,因为user_id像字符串一样,而不是存储id的变量。
我的路由器
scope "/", PxblogWeb do
pipe_through :browser
get "/", PageController, :index
resources "/users", UserController do
resources "/posts", PostController
end
resources "/sessions", SessionController, only: [:new, :create, :delete]
end
scope "/" do
resources "/sessions", SessionController, only: [:new, :create, :delete]
end
# Other scopes may use custom stacks.
scope "/api", PxblogWeb do
pipe_through :api
get "/users/read", UserController, :index
end
new.html.eex
<h1>New Post</h1>
<%= render "form.html", Map.put(assigns, :action, Routes.user_post_path(@conn, :create, :user_id)) %>
<span><%= link "Back", to: Routes.user_post_path(@conn, :index, :user_id) %></span>
post_controller.ex
plug :assign_user
plug :authorize_user when action in [:new, :create, :update, :edit, :delete]
defp assign_user(conn, _opts) do
case conn.params do
%{"user_id" => user_id} ->
case Repo.get(Pxblog.Post.User, user_id) do
nil -> invalid_user(conn)
user -> assign(conn, :user, user)
end
_ -> invalid_user(conn)
end
end
defp invalid_user(conn) do
conn
|> put_flash(:error, "Invalid user!")
|> redirect(to: Routes.page_path(conn, :index))
|> halt
end
defp authorize_user(conn, _) do
user = get_session(conn, :current_user)
if user && (Integer.to_string(user.id) == conn.params["user_id"] || Pxblog.RoleChecker.is_admin?(user)) do
conn
else
conn
|> put_flash(:error, "You are not authorized to modify that post!")
|> redirect(to: Routes.page_path(conn, :index))
|> halt()
end
end
def index(conn, _params) do
posts = Repo.all(assoc(conn.assigns[:user], :posts))
render(conn, "index.html", posts: posts)
end
def new(conn, _params) do
changeset =
conn.assigns[:user]
|> build_assoc(:posts)
|> Pxblog.Learn.Post.changeset(%{})
render(conn, "new.html", changeset: changeset)
end
def create(conn, %{"post" => post_params}) do
changeset =
conn.assigns[:user]
|> build_assoc(:posts)
|> Pxblog.Learn.Post.changeset(post_params)
case Repo.insert(changeset) do
{:ok, _post} ->
conn
|> put_flash(:info, "Post created successfully.")
|> redirect(to: Routes.user_post_path(conn, :index, conn.assigns[:user]))
{:error, changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
session_controller.ex
defmodule PxblogWeb.SessionController do
use PxblogWeb, :controller
alias Pxblog.Post.User
alias Pxblog.Post
alias Pxblog.Repo
import Comeonin.Bcrypt, only: [checkpw: 2, dummy_checkpw: 0]
plug :scrub_params, "user" when action in [:create]
def new(conn, _params) do
render conn, "new.html", changeset: Pxblog.Post.User.changeset(%Pxblog.Post.User{}, %{})
end
def create(conn, %{"user" => %{"username" => username, "password" => password}})
when not is_nil(username) and not is_nil(password) do
user = Repo.get_by(Pxblog.Post.User, username: username)
sign_in(user, password, conn)
end
def create(conn, _) do
failed_login(conn)
end
defp failed_login(conn) do
dummy_checkpw()
conn
|> put_session(:current_user, nil)
|> put_flash(:error, "Invalid username/password combination!")
|> redirect(to: Routes.page_path(conn, :index))
|> halt()
end
defp sign_in(user, _password, conn) when is_nil(user) do
failed_login(conn)
end
defp sign_in(user, password, conn) do
if checkpw(password, user.password_digest) do
conn
|> put_session(:current_user, %{id: user.id, username: user.username, role_id: user.role_id})
|> put_flash(:info, "Sign in successful!")
|> redirect(to: Routes.page_path(conn, :index))
else
failed_login(conn)
end
end
def delete(conn, _params) do
conn
|> delete_session(:current_user)
|> put_flash(:info, "Signed out successfully!")
|> redirect(to: Routes.page_path(conn, :index))
end
end
答案 0 :(得分:1)
您的标题中的错误表明某个地方存在一个类似于以下内容的字符串:
"some string user_id"
而不是:
"some string #{user_id}"
错误消息中的where
表示数据库查询,因此:
"...where id=user_id"
代替
"...where id=#{user_id}"
=====
在您的new
动作中,您具有:
def new(conn, _params) do
changeset =
conn.assigns[:user]
|> build_assoc(:posts)
|> Pxblog.Learn.Post.changeset(%{})
render(conn, "new.html", changeset: changeset)
end
render(
调用使变量@changeset
在模板中可用(以及长生不老药的@conn
)。这是您的模板:
<h1>New Post</h1>
<%= render "form.html",
Map.put(assigns, :action,
Routes.user_post_path(@conn, :create, :user_id)
)
%>
<span><%= link "Back",
to: Routes.user_post_path(@conn, :index, :user_id) %>
</span>
1)您无需在模板中使用@changeset
,因此无需在render()中指定changeset: changeset
。
2)我认为每个:user_id
原子都应该是一个变量,它包含实际的user_id-您应该在操作中创建并在render()中指定的变量,就像您对changeset
。
将原子内插到字符串(而不是变量)中时,得到:
iex(2)> "...where user=#{:user_id}"
"...where user=user_id"
错误消息指出子字符串“ user_id”无法转换为:id
类型。我认为Phoenix必须执行数据库查询以构造user_post_path
。
如果您这样做:
$ iex -S mix
然后:
iex(1)> h <YourApp>Web.Router.Helpers.user_post_path
def user_post_path(conn_or_endpoint, action, user_id)
def user_post_path(conn_or_endpoint, action, user_id, params)
def user_post_path(conn_or_endpoint, action, user_id, id, params)
iex显示为您的路径助手创建的实际定义。我认为,如果user_id
的参数应该是原子:user_id
,那么这些函数将在原子上进行模式匹配,如下所示:
def user_post_path(conn_or_endpoint, action, :user_id)
def user_post_path(conn_or_endpoint, action, :user_id, params)
def user_post_path(conn_or_endpoint, action, :user_id, id, params)