发布于 2026-01-06 2 阅读
0

Elixir API 和 Elm SPA - 第 1 部分 第 1 部分:Elixir 应用创建

Elixir API 和 Elm SPA - 第一部分

第一部分:Elixir 应用创建

第一部分:Elixir 应用创建

我打算创建一个演示应用,后端使用 Elixir API,前端使用独立的 Elm 单页应用 (SPA)。该应用将是一个简单的 CRUD 应用,并遵循我目前总结的最佳实践。它将是一个简单的市场应用,用户可以在这里发布待售物品的信息,其他用户可以查看并购买。
该应用的名称为 Toltec。

系列

  1. 第一部分 - Elixir 应用创建
  2. 第二部分 - 添加监护人身份验证
  3. 第三部分:Elm 应用创建和路由设置
  4. 第四部分:添加登录和注册页面
  5. 第五部分:将会话数据持久化到本地存储

假设

我假设你使用的是:

  • Elixir 1.6.5
  • Erlang 20
  • 榆树 0.18
  • PostgreSQL 10.4

创建应用

让我们为 API 创建一个简单的应用程序。我们不需要 HTML 或 Brunch,因为我们根本不渲染 HTML。我们只需要公开一个 JSON REST API:

mix phx.new toltec-api --app toltec --no-brunch --no-html --binary-id
Enter fullscreen mode Exit fullscreen mode

配置自动格式化

在添加任何代码之前,让我们先配置代码自动格式化。在项目根目录下添加一个名为.formatter.exs的文件:

# .formatter.exs

[
  inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
Enter fullscreen mode Exit fullscreen mode

然后运行格式化命令:

mix format
Enter fullscreen mode Exit fullscreen mode

这样应该可以格式化项目中的所有代码。

现在你需要配置编辑器,使其在保存文件时自动运行此命令。例如,对于 VS Code,你可以使用vscode-elixirvscode-elixir-formatter扩展。

创建账户

我们将使用comeonin包来处理应用程序中的密码哈希。请在 mix.exs 文件中添加此依赖项。

 # mix.exs

  defp deps do
    [
      {:phoenix, "~> 1.3.0"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_ecto, "~> 3.2"},
      {:postgrex, ">= 0.0.0"},
      {:gettext, "~> 0.11"},
      {:cowboy, "~> 1.0"},
      {:comeonin, "~> 4.0"},
      {:argon2_elixir, "~> 1.2"}
    ]
  end
Enter fullscreen mode Exit fullscreen mode

获取依赖项

mix deps.get
Enter fullscreen mode Exit fullscreen mode

现在让我们添加用户架构和帐户上下文来保存所有这些逻辑。

mix phx.gen.context Accounts User users name:string email:string:unique password_hash:string
Enter fullscreen mode Exit fullscreen mode

这将创建一个用户迁移、一个用户架构和一个账户上下文。对于用户,我不会使用 UUID 主键,因此我将从用户迁移中移除 binary_id 配置,但您可以根据需要保留它。
将迁移更改为:

# priv/repo/migrations/20180612062911_create_users.exs

  def change do
    execute("CREATE EXTENSION citext;")

    create table(:users) do
      add(:name, :string, null: false)
      add(:email, :citext, null: false)
      add(:password_hash, :string, null: false)

      timestamps()
    end

    create(unique_index(:users, [:email]))
  end
Enter fullscreen mode Exit fullscreen mode

接下来,我们向用户模式添加一些基本方法:

 # lib/toltec/accounts/user.ex

 defmodule Toltec.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset
  alias Toltec.Accounts.User

  schema "users" do
    field(:email, :string)
    field(:name, :string)
    field(:password, :string, virtual: true)
    field(:password_hash, :string)

    timestamps()
  end

  def changeset(%User{} = user, attrs) do
    user
    |> cast(attrs, [:name, :email])
    |> validate_required([:name, :email])
    |> validate_length(:name, min: 2, max: 255)
    |> validate_length(:email, min: 5, max: 255)
    |> unique_constraint(:email)
    |> validate_format(:email, ~r/@/)
  end

  def registration_changeset(%User{} = user, attrs) do
    user
    |> changeset(attrs)
    |> cast(attrs, [:password])
    |> validate_required([:password])
    |> validate_length(:password, min: 8, max: 100)
    |> put_password_hash()
  end

  defp put_password_hash(changeset) do
    case changeset do
      %Ecto.Changeset{valid?: true, changes: %{password: password}} ->
        put_change(changeset, :password_hash, Comeonin.Argon2.hashpwsalt(password))

      _ ->
        changeset
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

我添加了一个虚拟密码字段,用于临时存储明文密码。该密码永远不会保存到数据库中。我还添加了两个变更集,一个用于更改密码,另一个用于更改其他字段。`put_password_hash () ` 函数使用 comeonin 库获取密码的哈希值,并将其赋值给将要存储到数据库中的`password_hash`字段。

现在修改包含所有账户逻辑的账户上下文,使其如下所示:

# lib/toltec/accounts/accounts.ex

defmodule Toltec.Accounts do
  import Ecto.Query, warn: false
  alias Toltec.Repo

  alias Toltec.Accounts.User

  def list_users do
    Repo.all(User)
  end

  def get_user!(id), do: Repo.get!(User, id)

  def create_user(attrs \\ %{}) do
    result =
      %User{}
      |> User.registration_changeset(attrs)
      |> Repo.insert()

    case result do
      {:ok, user} -> {:ok, %User{user | password: nil}}
      _ -> result
    end
  end

  def update_user(%User{} = user, attrs) do
    user
    |> User.changeset(attrs)
    |> Repo.update()
  end

  def delete_user(%User{} = user) do
    Repo.delete(user)
  end

  def change_user(%User{} = user) do
    User.changeset(user, %{})
  end
end
Enter fullscreen mode Exit fullscreen mode

接下来,让我们向数据库添加一些种子数据,使我们的应用程序可以使用。

# priv/repo/seeds.exs

# users
user =
  Toltec.Accounts.User.registration_changeset(%Toltec.Accounts.User{}, %{
    name: "some user",
    email: "user@toltec",
    password: "user@toltec"
  })

Toltec.Repo.insert!(user)

Enter fullscreen mode Exit fullscreen mode

现在是时候构建数据库、创建表并插入种子数据了。

mix ecto.reset
Enter fullscreen mode Exit fullscreen mode

此命令会删除数据库(如果存在),然后重新创建数据库,按顺序运行迁移,最后运行 seeds.exs 文件插入初始数据。输出结果应该与此类似,并且不会出现任何错误。

mix ecto.reset
The database for Toltec.Repo has already been dropped
The database for Toltec.Repo has been created
[info] == Running Toltec.Repo.Migrations.CreateUsers.change/0 forward
[info] execute "CREATE EXTENSION citext;"
[info] create table users
[info] create index users_email_index
[info] == Migrated in 0.0s
[debug] QUERY OK db=2.8ms
INSERT INTO "users" ("email","name","password_hash","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5) RETURNING "id" ["user@toltec", "some user", "$argon2i$v=19$m=65536,t=6,p=1$jdZHJ4HdGSF34hP6iTiPeQ$+PWBp77RP8wxMasVj0wv1wWS33pponixKSEG109V/u8", {{2018, 6, 12}, {22, 35, 55, 411334}}, {{2018, 6, 12}, {22, 35, 55, 411345}}]
Enter fullscreen mode Exit fullscreen mode

您可以在此处仓库的 part-01 分支中找到源代码和测试

克隆完成后,运行测试并验证一切是否正常:

mix test
...................

Finished in 0.3 seconds
19 tests, 0 failures
Enter fullscreen mode Exit fullscreen mode

目前我们已经创建了应用程序、用户架构,并添加了一些初始用户来试用我们的应用程序。
暂时就这些。

文章来源:https://dev.to/miguelcoba/elixir-api-and-elm-spa-4hpf