Java Tips | 文字列処理:URL形式チェック

Java Java
スポンサーリンク

URL形式チェックは「“リンクとして成立しているか”を早めにはじく」ための技

ユーザーに「ホームページURL」「コーポレートサイト」「SNSのURL」などを入力してもらう場面、結構ありますよね。
ここで何もチェックしないと、aaaexample のような、明らかにURLじゃない文字列がDBに入ってしまいます。

後でリンクとして表示しても飛べないし、外部連携でエラーになったりもします。
そこで使うのが URL形式チェック
これは「この文字列が“URLとしてあり得る形かどうか”をざっくり判定する」ためのユーティリティです。

完璧な判定ではなく、まずは
「明らかにおかしいものを早めにはじく」
というフィルタだと考えると、ちょうどいいです。


まずは「URLのざっくりルール」を押さえる

完璧を目指さず“現場で困らないライン”を決める

URLの正式な仕様(RFC)はかなり複雑です。
http, https だけでなく、ftp, mailto, tel などもあるし、
クエリパラメータやフラグメント、IPv6アドレス、IDN(日本語ドメイン)なども絡んできます。

でも、実務でそこまで全部を完璧に扱おうとすると、
正規表現が怪物になって、誰も読めなくなります。

だからまずは、こんな「ざっくりルール」を決めてしまうのが現実的です。

スキーム(http://https://)が付いていること
その後にホスト名(example.com など)があること
最低限、「http://example.com ならOK」「example.com だけはNG」くらいの線引き

このくらいでも、「明らかにURLじゃないもの」はかなり弾けます。


Java標準の java.net.URL を使ったシンプルなチェック

「パースできるかどうか」で判定する

URL形式チェックで、まず覚えてほしいのは
「自分で正規表現を書かなくても、Java標準ライブラリでかなり戦える」
ということです。

java.net.URL クラスは、文字列をURLとして解釈できるかどうかをチェックしてくれます。

import java.net.MalformedURLException;
import java.net.URL;

public final class UrlValidator {

    private UrlValidator() {}

    public static boolean isValidUrl(String url) {
        if (url == null || url.isBlank()) {
            return false;
        }
        try {
            new URL(url);
            return true;
        } catch (MalformedURLException e) {
            return false;
        }
    }
}
Java

使い方はこうなります。

System.out.println(UrlValidator.isValidUrl("https://example.com"));          // true
System.out.println(UrlValidator.isValidUrl("http://example.com/path"));      // true
System.out.println(UrlValidator.isValidUrl("ftp://example.com/file.txt"));   // true

System.out.println(UrlValidator.isValidUrl("example.com"));                  // false
System.out.println(UrlValidator.isValidUrl("http//example.com"));            // false
System.out.println(UrlValidator.isValidUrl("http:/example.com"));            // false
System.out.println(UrlValidator.isValidUrl(""));                             // false
Java

ここで深掘りしたい重要ポイントは二つです。

一つ目は、「“URLとしてパースできるかどうか”を、例外の有無で判定している」ことです。
new URL(url) が成功すれば「形式としてはOK」、
MalformedURLException が投げられたら「形式としてNG」と判断しています。

二つ目は、「この時点では“スキームの種類”までは絞っていない」ことです。
ftp://file:/ なども、URLとしては有効なので true になります。
もし「httphttps だけ許可したい」なら、もう一段階絞り込みが必要です。


http / https だけに絞りたい場合

スキームをチェックして“Web用URL”に限定する

多くのWebシステムでは、
「ユーザーに入力してほしいのは httphttps のURLだけ」
というケースが多いです。

その場合は、URL でパースしたあとに、スキームをチェックします。

import java.net.MalformedURLException;
import java.net.URL;

public final class UrlValidator {

    private UrlValidator() {}

    public static boolean isHttpOrHttps(String url) {
        if (url == null || url.isBlank()) {
            return false;
        }
        try {
            URL u = new URL(url);
            String protocol = u.getProtocol();
            return "http".equalsIgnoreCase(protocol) || "https".equalsIgnoreCase(protocol);
        } catch (MalformedURLException e) {
            return false;
        }
    }
}
Java

使い方はこうです。

System.out.println(UrlValidator.isHttpOrHttps("https://example.com"));   // true
System.out.println(UrlValidator.isHttpOrHttps("http://example.com"));    // true

System.out.println(UrlValidator.isHttpOrHttps("ftp://example.com"));     // false
System.out.println(UrlValidator.isHttpOrHttps("file:/path/to/file"));    // false
System.out.println(UrlValidator.isHttpOrHttps("example.com"));           // false
Java

ここでのポイントは、「“URLとして成立しているか”と“自分たちが許したいURLか”を分けて考える」ことです。

isValidUrl は「URLとして成立しているか」を見ているだけ。
isHttpOrHttps は「その中から、Web用URLだけを許可する」というフィルタです。

この二段構えにしておくと、
用途に応じてどちらを使うか選べるので、コードの意図がとても分かりやすくなります。


正規表現で「最低限の形」だけをチェックする方法

ライブラリを使えない場面向けの“軽量版”

何らかの理由で java.net.URL を使いたくない、
あるいは「もっとざっくりでいいから軽くチェックしたい」という場合は、
正規表現で「最低限の形」だけを見る方法もあります。

例えば、こんなルールを考えます。

http:// または https:// で始まる
その後に、空白を含まない文字が1文字以上続く

これを正規表現にすると、こうなります。

import java.util.regex.Pattern;

public final class UrlValidatorRegex {

    private UrlValidatorRegex() {}

    private static final Pattern SIMPLE_HTTP_URL =
            Pattern.compile("^https?://\\S+$");

    public static boolean isSimpleHttpUrl(String url) {
        if (url == null || url.isBlank()) {
            return false;
        }
        return SIMPLE_HTTP_URL.matcher(url).matches();
    }
}
Java

使い方はこうです。

System.out.println(UrlValidatorRegex.isSimpleHttpUrl("https://example.com"));      // true
System.out.println(UrlValidatorRegex.isSimpleHttpUrl("http://example.com/path"));  // true

System.out.println(UrlValidatorRegex.isSimpleHttpUrl("ftp://example.com"));        // false
System.out.println(UrlValidatorRegex.isSimpleHttpUrl("example.com"));              // false
System.out.println(UrlValidatorRegex.isSimpleHttpUrl("http://exa mple.com"));      // false(空白NG)
Java

ここでの重要ポイントは、「この正規表現は“かなりざっくり”だという自覚を持つこと」です。
ホスト名が正しいかどうか、ポート番号が正しいかどうか、
パスやクエリの中身が正しいかどうかまでは見ていません。

あくまで「http://https:// で始まっていて、空白が含まれていない」
という最低限の条件だけを見ています。


「形式チェック」と「到達可能かどうか」は別物だと理解する

URL形式チェックユーティリティがやっているのは、
あくまで「文字列の形がURLとして成立しているかどうか」の判定だけです。

https://example.com/unknown が形式的に正しくても、
そのURLにアクセスしたときに 404 になるかもしれません。

「本当に存在するページかどうか」を知りたければ、
実際にHTTPリクエストを投げてステータスコードを見る必要があります。

ここで大事なのは、
「形式チェックは“明らかにおかしいものを弾くフィルタ”であって、“正しいことの保証”ではない
という感覚を持っておくことです。


例題:入力フォームでのURL形式チェック

ユーザー登録フォームで、「ホームページURL」を入力してもらう場面を考えます。

サーバ側では、こんな感じで使えます。

String url = request.getParameter("homepageUrl");

if (url == null || url.isBlank()) {
    // 任意入力ならスキップ、必須ならエラー
    // errors.add("ホームページURLを入力してください。");
} else if (!UrlValidator.isHttpOrHttps(url.trim())) {
    errors.add("URLの形式が正しくありません。(例:https://example.com)");
}
Java

ここでのポイントは二つです。

一つ目は、「未入力かどうか」と「形式が正しいかどうか」を分けている」ことです。
任意入力なら、未入力はOK・形式だけチェック、
必須入力なら、未入力もエラー、というように分けて考えられます。

二つ目は、「チェックに通ったら、そのままDBに保存してもよい状態になっている」ことです。
少なくとも「http / https で始まる」「URLとしてパースできる」
という条件を満たしているので、
後続処理で「明らかにおかしい文字列」に悩まされる可能性がかなり減ります。


まとめ:URL形式チェックユーティリティで身につけたい感覚

URL形式チェックは、「完璧な判定」を目指すものではなく、
「明らかにおかしいものを早めにはじくためのフィルタ」 です。

まずは、

java.net.URL を使って「パースできるかどうか」で判定する
必要なら、スキームを見て http / https に絞る

というシンプルなユーティリティを用意してみてください。

そのうえで、どうしても正規表現でやりたい場面では、
^https?://\\S+$ のような「最低限の形」だけを見る軽量版を使う。

もしあなたのコードのどこかに、

String url = request.getParameter("homepageUrl");
// そのままDBに保存
Java

のような箇所があれば、
そこを題材にして、ここで作った UrlValidator.isHttpOrHttps を一度挟んでみてください。

それだけで、「明らかにURLじゃない文字列がシステムに入り込む」リスクを、かなり減らすことができます。

タイトルとURLをコピーしました