WebアプリケーションのURLの末尾に "/" (トレイリングスラッシュ) が含まれていない場合にうまく動作しない

WebアプリケーションのURLの末尾に "/" (トレイリングスラッシュ) が含まれていない場合にうまく動作しない現象について紹介します。

現象の確認

作成したWebアプリケーションを実行するマシンに配置し、以下のURLにマッピングしました。
下図のURLの場合は正しくアクセスできます。
http://(ホスト名)/(ディレクトリ名)/app/

しかし、以下のURLでアクセスした場合に、アプリケーションルートのページは表示されますが、 画像が表示されなかったり、参照しているJavaScriptのコードが実行できない場合があります。
http://(ホスト名)/(ディレクトリ名)/app

原因

アプリケーションルートのページのリンクが相対リンクの場合、たとえばimg/image01.png の場合、トレイリングスラッシュがある場合は次のURLになりアクセスできます。
http://(ホスト名)/(ディレクトリ名)/app/img/image01.png
一方、トレイリングスラッシュがない場合は、app部分はディレクトリと判定されないため、以下のURLとなり、ファイルが見つからず、アクセスできません。
http://(ホスト名)/(ディレクトリ名)/img/image01.png
補足
ただし、ドメインルートがアプリケーションルートの場合にはこの問題は発生せず、アプリケーションルートが以下の場合では、
http://(ホスト名)

トレイリングスラッシュがある場合も、トレイリングスラッシュがない場合でもimg/image01.pngのリンク先は、以下のURLになります。
http://(ホスト名)/img/image01.png

対処法1:リダイレクトする

トレイリングスラッシュなしでアプリケーションルートにアクセスした際に、トレイリングスラッシュありのURLにリダイレクトさせます。
http://www.ipentec.net/testapp/app
にアクセスした場合、トレイリングスラッシュありの下記のURLにリダイレクトします。
http://www.ipentec.net/testapp/app/

ただし、ASP.NET WebFormアプリケーションの場合は、IISに設定してある[既定のドキュメント]に default.aspx を追加することで、 ディレクトリに対するアクセスhttp://www.ipentec.net/testapp/apphttp://www.ipentec.net/testapp/app/default.aspx へのアクセスとなり、 自動でhttp://www.ipentec.net/testapp/app/ へのリダイレクトが動作するため、問題になることは少ないです。
ASP.NET WebFormアプリケーションで独自にルーティングを実装している場合には、問題が発生する場合があるため、トレイリングスラッシュありのURLの リダイレクトを実装する必要があります。

ASP.NET Core アプリケーションの場合はアプリケーションルート以下のすべてのアクセスはアプリケーションへのアクセスとなるため、 トレイリングスラッシュありのURLへのリダイレクト処理の実装が必要になりますが、ASP.NET Coreアプリケーションでトレイリングスラッシュ ありのURLにリダイレクトする方法はあまり良い方法ではないです。

対処法2:どちらのURLでも動作するように実装する

以下のどちらのURLにアクセスした場合でも同じページを表示し、リンク切れも起きないように実装する方法です。
http://www.ipentec.net/testapp/app
http://www.ipentec.net/testapp/app/
ASP.NET アプリケーションでは、URLに ~ 演算子が使用でき、アプリケーションルートからの相対URLを記述できるため、 トレイリングスラッシュあり、なし、どちらのURLにアクセスした場合でも RazorPages内であれば、~/img/image01.png のURLであれば、 http://(ホスト名)/img/image01.png を参照できます。またリンクも同様に ~ 演算子が利用できます。
また、リダイレクトやページ遷移の場合も、Redirectメソッドではなく、RedirectToPageメソッドを利用し実URLではなく、RazorPageの名称でリダイレクトすれば、 トレイリングスラッシュを意識せずに済みます。 その他、絶対参照URLを利用し相対参照URLを使用しない、動的に生成されるリンクで相対URLを利用する場合は、 アプリケーションを配置したディレクトリのプリフィックスを利用してリンクを生成する処理をして、 トレイリングスラッシュあり、なし、どちらでも動作するページを実装できます。

ASP.NET Core アプリケーションの場合は、こちらの方法がおすすめです。

プロキシがある場合の対処法:URL書き換えやリダイレクト時にトレイリングスラッシュを含めない

上記の対策に加え、リバースプリキシやURL書き換えをする場合には、 URLの書き換えやリダイレクト時にトレイリングスラッシュを含めないことで対処できます。

以下URLを
http://www.ipentec.net/testapp/app
下記URLに書き換える場合、
http://192.168.0.1/testapp/app

www.ipentec.net/testapp/app192.168.0.1/testapp/app と書き換えることで対応できます。
http://www.ipentec.net/testapp/app にアクセスした場合、URLの書き換えで、http://192.168.0.1/testapp/app に変わり、 アプリケーションのディレクトリアクセス時にトレイリングスラッシュありにリダイレクトされ、http://192.168.0.1/testapp/app/ の リダイレクトになり、結果 http://www.ipentec.net/testapp/app/ にリダイレクトされる動作となります。

うまくいかない例

www.ipentec.net/testapp/app192.168.0.1/testapp/app/ とトレイリングスラッシュありで書き換えてしまう場合、 アプリケーション側でのリダイレクトが発生しないため、http://www.ipentec.net/testapp/app のURLで http://192.168.0.1/testapp/app/ のコンテンツが表示されるため、 リンクの不整合が発生してしまいます。

参考:YARPでのappsettings.json での記述例

DestinationのAddressにトレイリングスラッシュを含めないで記述します。
{

  "ReverseProxy": {

    "Routes": {
      "TestRoute": {
        "ClusterId": "TestCluster",
        "Match": {
          "Path": "/testapp/{**any}"
        },
        "Transforms": [
          {
            "PathRemovePrefix": "/testapp"
          }
        ]
      }
    },
    "Clusters": {
      "TestCluster": {
        "Destinations": {
          "testapp": {
            "Address": "http://192.168.0.1/testapp"
          }
        }
      }
    }
  }

}

以下のトレイリングスラッシュを含めた記述にすると、冒頭に紹介したリンク切れが発生します。
    "Clusters": {
      "TestCluster": {
        "Destinations": {
          "testapp": {
            "Address": "http://192.168.0.1/testapp/"
          }
        }
      }
    }
著者
iPentec.com の代表。ハードウェア、サーバー投資、管理などを担当。
Office 365やデータベースの記事なども担当。
最終更新日: 2022-11-26
作成日: 2022-07-10
iPentec all rights reserverd.