jpcaruana
How to do a series of validations in Elixir the most idiomatic way
I am new (but curious) to Elixir. I want to validate two aspects of an URL :
- it should not be an IP adress
- the path should not contains
/../
I have a function:
def validate_url(url) do
URI.parse(url)
|> validate_profile
end
After some pattern matching (to exclude other cases), I come to 2 (unstatisfying) solutions :
1st :
defp validate_profile(%URI{scheme: "https", path: path, host: host, userinfo: nil, fragment: nil}),
do: validate_path(path)
|> validate_host(host)
<snip code>
defp validate_profile(_), do: :invalid
defp validate_path(path) do
case String.split(path, ["/../"]) do
[_ | []] -> :valid
_ -> :invalid
end
end
defp validate_host(:invalid, _), do: :invalid
defp validate_host(:valid, host) do
case :inet.parse_address(to_charlist(host)) do
{:ok, _} -> :invalid
_ -> :valid
end
end
2nd:
defp validate_profile(%URI{scheme: "https", path: path, host: host, userinfo: nil, fragment: nil}),
do: validate_path(path, host)
|> validate_host
<snip code>
defp validate_profile(_), do: :invalid
defp validate_path(path, host) do
case String.split(path, ["/../"]) do
[_ | []] -> host
_ -> :invalid
end
end
defp validate_host(:invalid), do: :invalid
defp validate_host(host) do
case :inet.parse_address(to_charlist(host)) do
{:ok, _} -> :invalid
_ -> :valid
end
end
How can I improve here ? Is there a more idiomatic way of doing this ?
Marked As Solved
darkbaby123
Below is an example to use with. Each validation step only return ok/error tuple. The url is not changed so it can be reused in each step.
@spec validate_url(raw_url :: URI.t() | binary) :: {:ok, URI.t()} | :error
def validate_url(raw_url) do
with url <- URI.parse(raw_url),
:ok <- validate_path(url),
:ok <- validate_host(url) do
{:ok, url}
else
:error -> :error
end
end
@spec validate_path(URI.t()) :: :ok | :error
defp validate_path(%URI{path: path}) do
# ...
end
@spec validate_host(URI.t()) :: :ok | :error
defp validate_host(%URI{host: host}) do
# ...
end
Pipeline is suitable when you have a data structure to express both url and validation result. The simplest data structure is {:ok, url} | :error .
Also Liked
dimitarvp
This could be a good candidate for using the with keyword.
wanton7
I’m no Elixir guru but probably something like this
def parse_url(url) do
{:ok, URI.parse(url)}
|> validate_profile()
|> validate_stuff()
end
defp validate_profile({:ok, url} = data) do
# validate here
# if ok
data
# if error
{:error, "profile validation failed"}
end
defp validate_stuff({:ok, url} = data) do
# validate here
# if ok
data
# if error
{:error, "stuff validation failed"}
end
defp validate_stuff(x), do: x
In case you need to return nil or string if it’s valid use something like
def parse_url(url) do
{:ok, URI.parse(url)}
|> validate_profile()
|> validate_stuff()
|> case do
{:ok, url} -> url
_ -> nil
end
end
NobbZ
Why?
I can understand why you do not want the server part to be an IP. But why restricting the path to not contain /../?
http://example.com/path/../foo and http://example.com/foo are different URLs. Not every server will interprete .. as “one folder up”.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance










