Reputation: 15146
I want to validate uris like:
http://vk.com
http://semantic-ui.com/collections/menu.html
https://translate.yandex.ru/?text=poll&lang=en-ru
and not
www.vk.com
abdeeej
http://vk
but haven't found either package of native code implementation for it.
How can I do that?
Upvotes: 7
Views: 5071
Reputation: 177
Since elixir 1.13.0
, another way to validate a url is using URI.new/1
. It errors on poorly encoded urls and more stricter compared to URI.parse
Caution: URI.new/1 fails on poorly encoded or not encoded url such as url with a space in it which is not encoded and returns error.
# Without encoding, notice the space in the url
iex(1)> URI.new("https://testdomain/Teams/Odense Boldklub.png")
{:error, ":"}
# After encoding URL
iex(9)> URI.encode("https://testdomain/Teams/Odense Boldklub.png") |> URI.new
{:ok,
%URI{
scheme: "https",
userinfo: nil,
host: "testdomain",
port: 443,
path: "/Teams/Odense%20Boldklub.png",
query: nil,
fragment: nil
}}
Upvotes: 2
Reputation: 6784
This solution is more complete:
def validate_url(changeset, field, opts \\ []) do
validate_change(changeset, field, fn _, value ->
case URI.parse(value) do
val(%URI{scheme: nil}) ->
"is missing a scheme (e.g. https)"
%URI{host: nil} ->
"is missing a host"
%URI{host: host} ->
case :inet.gethostbyname(Kernel.to_charlist(host)) do
{:ok, _} -> nil
{:error, _} -> "invalid host"
end
end
|> case do
error when is_binary(error) -> [{field, Keyword.get(opts, :message, error)}]
_ -> []
end
end)
end
Cribbed from https://gist.github.com/atomkirk/74b39b5b09c7d0f21763dd55b877f998
Upvotes: 4
Reputation: 222080
All of those are technically valid URLs according to the spec so URI.parse/1
returns a %URI{}
struct for all, but if you want to reject domains without a scheme
and a dot in the host
, you can do:
valid? = fn url ->
uri = URI.parse(url)
uri.scheme != nil && uri.host =~ "."
end
Test:
urls = [
"http://vk.com",
"http://semantic-ui.com/collections/menu.html",
"https://translate.yandex.ru/?text=poll&lang=en-ru",
"www.vk.com",
"abdeeej",
"http://vk"
]
urls |> Enum.map(valid?) |> IO.inspect
Output:
[true, true, true, false, false, false]
Upvotes: 15