C#

RelativeUriのこと

ウェブページ(HTML)のソースコードに記載されたURLを抽出する場合、リンクのURLが実際にブラウザでどのようなURLになるか見てみたほうが良い。UriクラスのRelativeUriの扱いは、ブラウザの相対URLとはちょっと違うようだ。むやみにUriのコンストラクタを使うと、うまくいかないことがある。

Uri a = new Uri("http://hoge.com/dir1/dir2/file1.html?a=b&c=d#e");
Uri b = new Uri("?x=y", UriKind.Relative);
Uri c = new Uri(a, b);
Debug.WriteLine(c);

----
http://hoge.com/dir1/dir2/?x=y

もし、「a」のURLのページに「b」のリンクを記載したなら、ブラウザでは「http://hoge.com/dir1/dir2/file1.html?x=y」になるだろう。私もそのURLを取得するメソッドが欲しいと思った。

Uri MakeAbsoluteUrl(Uri PageUrl, string HRef)
{
    if ((HRef != null) && HRef.StartsWith("?"))
    {
        HRef = PageUrl.AbsolutePath + HRef;
    }

    return new Uri(PageUrl, HRef);
}

Uri MakeAbsoluteUrl(Uri PageUrl, Uri LinkUrl)
{
    return MakeAbsoluteUrl(PageUrl, LinkUrl.OriginalString);
}

一方で、相対URLを作る場合の課題は些細だ。基本的にブラウザと違いはないが、ちょっとだけ格好悪い。

Uri a = new Uri("http://hoge.com/dir1/dir2/file1.html?a=b&c=d#e");
Uri b = new Uri("http://hoge.com/dir1/dir2/file1.html?a=b&c=d#z");
Uri c = a.MakeRelativeUri(b);
Debug.WriteLine(c);

----
?a=b&c=d#z

ハッシュ(#)はページ内の位置(Anchor)を示している。クエリまで同じならページとしても同じなので、相対URLにクエリ部分は不要だ。できたら「#z」だけになってほしいと思う。課題は些細なのに、格好良くしようとすると、ちょっと面倒。

Uri MakeRelativeUrl(Uri PageUrl, Uri LinkUrl)
{
    if (!(LinkUrl.IsAbsoluteUri))
    {
        LinkUrl = MakeAbsoluteUrl(PageUrl, LinkUrl.OriginalString);
    }

    Uri RelativeUrl = PageUrl.MakeRelativeUri(LinkUrl);

    if (RelativeUrl.OriginalString.StartsWith("?") &&
        (LinkUrl.Query == PageUrl.Query) &&
        (LinkUrl.Fragment != string.Empty))
    {
        RelativeUrl = new Uri(LinkUrl.Fragment, UriKind.Relative);
    }

    return RelativeUrl;
}

Uri MakeRelativeUrl(Uri PageUrl, string HRef)
{
    Uri LinkUrl = new Uri(HRef, UriKind.RelativeOrAbsolute);
    return MakeRelativeUrl(PageUrl, LinkUrl);
}

サイト内リンクを相対URLに変換するのも、やってみるとなかなか大変だ。

(2018/11/10 初稿)