Parse và validate request param trong Phoenix dùng Ecto.Changeset
#TIL
11
elixir
40
phoenix
4
White

Dung Nguyen viết ngày 26/09/2020

Khi viết các API hoặc cả các endpoint thì thông thường chúng ta sẽ có một số nhu cầu:

  • Chỉ cho phép một số các tham số xác định được truyền vào.
  • Chuyển các tham số về kiểu dữ liệu mong muốn
  • Validate các tham số theo yêu cầu

Bài viết này sẽ hướng dẫn các bạn giải quyết các vấn đề trên sử dụng Ecto.Changeset

Thư viện Ecto đã cung cấp sẵn cho chúng ta module Changeset. Nó hỗ trợ việc cast các tham số về đúng kiểu dữ liệu mong muốn, nó cũng hỗ trợ các phương thức để validate các tham số yêu cầu, và nó cũng cho phép bạn giới hạn tham số nào được truyền vào.

Và sau đây là một ví dụ sử dụng Chageset để validate các tham số khi filter các đơn hàng.

1. Đầu tiên bạn phải định nghĩa một schema

defmodule MyApp.OrderFilterParams do
    use Ecto.Schema
    import Ecto.Changeset

    schema "order_filter_params" do
        field :keyword, :string
        field :category_id,  :integer
        field :status, :string
        field :start_date, :utc_datetime
        field :end_date, :utc_datetime
    end
end

2. Cast và validate

Sau đó phải định nghĩa một hàm để thực hiện việc cast tham số và validate changeset.

defmodule MyApp.OrderFilterParams do

    ...

    @required ~w(category_id start_date)
    @optional ~w(keyword status end_date)

    def changeset(changeset_or_model, params) do
         cast(changeset_or_model, params, @required ++ @optional)
        |> validate_required(@required)
    end
end

3. Set giá trị default động

Nếu bạn muốn sử dụng các giá trị default động, ví dụ như mặc định ngày kết thúc là ngày hiện tại, các bạn phải định nghĩa một function để set giá trị mong muốn.

defmodule MyApp.OrderFilterParams do

    ...

    def changeset(changeset_or_model, params) do
         cast(changeset_or_model, params, @required ++ @optional)
        |> validate_required(@required)
        |> set_default_end_date()
    end

    defp set_defaut_end_date(changeset) do
        end_date = get_change(changeset, :end_date)
        if is_nil(end_date) do
            put_change(changeset, :end_date, Timex.today())
        else
            changeset
        end
    end
end

4. Sử dụng Params schema

defmodule MyApp.OrderController do
    use MyApp, :controller
    alias MyApp.OrderFilterParams

    def index(conn, params) do
        changeset = OrderFilterParams.changeset(%OrderFilterParams{}, params)

        if changeset.valid? do
            strong_params = Ecto.Changeset.apply_changes(changeset)
                IO.put(strong_params.keyword)
            # Do something with your params
        else
            # handle error
        end
    end
end

Rất đơn giản đúng không, nếu bạn đã sử dụng Ecto thì việc này chỉ là ruồi muỗi. Tuy nhiên đơn giản thì phải có thứ đánh đổi chứ.

Vài thứ mà bạn sẽ thấy bất tiện

1. Lượng code mà bạn phải viết quá nhiều.

Thử tưởng tượng mỗi API bạn lại phải định nghĩa thêm một Module params cho nó thì phức tạp vl.

Bạn có thể sử dụng schemaless, nhưng mà function của bạn sẽ rối nùi lên vì code logic và code xử lý params nó không liên quan gì tới nhau cả. Và bạn thì kiểu như đổ sting vào cơm để ăn vậy.

2. Thiếu linh hoạt.

Điều này cũng đúng vì mục đích chính của Ecto là phục vụ cho việc định nghĩa các schema cho database.

Đơn giản như việc định nghĩa giá trị default động như trên, bạn phải viết luôn 1 hàm mới

Tuy nhiên nó cũng có một ưu điểm là bạn không phải sử dụng thêm thư viện của bên thứ ba.

Kết

Nếu bạn không cần phải xử lý nhiều ràng buộc liên quan đến tham số của request thì đơn giản là cứ dùng Changeset thôi.

Nếu bạn muốn nhanh gọn hơn thì trên Hex có một số thư viện để hỗ trợ định nghĩa param đơn giản hơn, ví dụ như https://github.com/bluzky/tarams/

Thư viện này cung cấp cách thức đơn giản và nhanh chóng hơn để định nghĩa param cho API. Mình sẽ viết bài hướng dẫn sau.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Dung Nguyen

6 bài viết.
2 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
6 0
Bài viết gốc trên (Link) ETS là gì? Có lẽ các bạn đã nghe qua về redis hoặc memcache, hoặc là cả hai. Còn nếu bạn chưa nghe tới bao giờ thì đó l...
Dung Nguyen viết hơn 2 năm trước
6 0
White
4 0
1. Tìm hiểu về FSM (Link) Máy trạng thái hữu hạn là một mô hình toán học biểu diễn trạng thái của hệ, trong đó số trạng thái là hữu hạn. Từ mỗi t...
Dung Nguyen viết hơn 2 năm trước
4 0
White
3 4
Bài viết này đuợc lấy từ blog cá nhân http://bluzky.github.io/ Trong quá trình tìm hiểu về lập trình chat bot sử dụng các API của Facebook Messen...
Dung Nguyen viết hơn 2 năm trước
3 4
Bài viết liên quan
White
1 0
Xem bài viết gốc trên (Link) Bài viết này sẽ hướng dẫn các bạn sử dụng ETS như là bộ nhớ cache để tăng tốc các ứng dụng web Phoenix Dành cho ...
Dung Nguyen viết hơn 2 năm trước
1 0
White
2 0
Yêu cầu chuẩn hoá và validate các tham số truyền lên từ client là yêu cầu cơ bản khi xây dựng API Web. Mình đã có một bài hướng dẫn sử dụng Ecto.Ch...
Dung Nguyen viết 2 tháng trước
2 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
6 bài viết.
2 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!