在`where`中的值`“ user_id”`无法在查询中转换为类型:id:

时间:2019-07-01 13:08:11

标签: elixir phoenix-framework rbac

我正在尝试开发基于角色的访问控制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

1 个答案:

答案 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)