アプリケーションルートがドメインルートにない場合のリンクの動作と実装方法を紹介します。
概要
アプリケーションのルートがサーバーのルートでない場合、アプリケーションルートのurlの末尾に "/" が無い場合に意図しない動作になる場合があります。
この記事では現象を確認しつつ、対処方法を紹介します。
現象の確認
RazorPagesのアプリケーションで以下のアプリケーションを作成します。
@page "/"
@model TrailingSlash.Pages.Test1Model
@{
}
<html>
<head>
</head>
<body>
<h2>テストです。</h2>
<a href="sub/Sub1">リンク1</a><br/>
<a href="/sub/Sub1">リンク2</a><br/>
<a href="./sub/Sub1">リンク3</a><br/>
<a href="/myapp/sub/Sub1">リンク4</a><br/>
</body>
</html>
@page "/sub/Sub1"
@model TrailingSlash.Pages.Sub1Model
@{
}
<html>
<head>
</head>
<body>
<p>テストです。</p>
</body>
</html>
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Routing;
namespace TrailingSlash
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
}
解説
Main1.cshtml
は
@page "/"
を記述してアプリケーションルートのページとします。
Sub1.cshtml
は
@page "/sub/Sub1" を記述し
https://(アプリケーションルート)/sub/Sub1` のURLのページとします。
Main1.cshtmlに Sub1 ページへのリンクを設置し動作を確認します。
動作結果
アプリケーションURLがドメインルートでない場合
アプリケーションのURLを
https://(ドメインルート)/myapp
とします。
https://(ドメインルート)/myapp の場合
プロジェクトを実行し、Webブラウザで
https://(ドメインルート)/myapp
のURLを開きます。Main1.cshtmlのページが表示されます。それぞれのリンクの動作を確認します。
[リンク1]をクリックします。
https://(ドメインルート)/sub/Sub1
への遷移となり、NotFoundとなります。
[リンク2]をクリックします。
https://(ドメインルート)/sub/Sub1
への遷移となり、NotFoundとなります。
[リンク3]をクリックします。
https://(ドメインルート)/sub/Sub1
への遷移となり、NotFoundとなります。
[リンク4]をクリックします。
https://(ドメインルート)/myapp/sub/Sub1
への遷移となり、Sub1 のページが表示されます。
https://(ドメインルート)/myapp/ の場合
プロジェクトを実行し、Webブラウザで
https://(ドメインルート)/myapp/
のURLを開きます。URLの末尾に"/"を追加するとどのように動作が変わるかを確認します。
Main1.cshtmlのページが表示されます。それぞれのリンクの動作を確認します。
[リンク1]をクリックします。
https://(ドメインルート)/myapp/sub/Sub1
への遷移となり、Sub1のページが表示されます。
[リンク2]をクリックします。
https://(ドメインルート)/sub/Sub1
への遷移となり、NotFoundとなります。
[リンク3]をクリックします。
https://(ドメインルート)/myapp/sub/Sub1
への遷移となり、Sub1のページが表示されます。
[リンク4]をクリックします。
https://(ドメインルート)/myapp/sub/Sub1
への遷移となり、Sub1のページが表示されます。
URLの末尾に"/" (トレイリングスラッシュ) があるかないかでリンクの動作が変わることが確認できました。
アプリケーションURLがドメインルートの場合
アプリケーションのURLを
https://(ドメインルート)
とします。
プロジェクトを実行し、Webブラウザで
https://(ドメインルート)
のURLを開きます。Main1.cshtmlのページが表示されます。それぞれのリンクの動作を確認します。
[リンク1]をクリックします。
https://(ドメインルート)/sub/Sub1
への遷移となり、Sub1のページが表示されます。
[リンク2]をクリックします。
https://(ドメインルート)/sub/Sub1
への遷移となり、Sub1のページが表示されます。
[リンク3]をクリックします。
https://(ドメインルート)/sub/Sub1
への遷移となり、Sub1のページが表示されます。
[リンク4]をクリックします。
https://(ドメインルート)/myapp/sub/Sub1
への遷移となり、HTTP Error 500.35のページが表示されます。
アプリケーションルートが、ドメイン直下かそうでないかでリンクの動作が変わることが確認できました。末尾に"/"が付くことで、相対URLが正しいリンク先に遷移できるようになります。
対処法1 : "/" を付加するURLにリダイレクトする
Main1.cshtml のページモデルクラスを下記のコードに変更します。
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace TrailingSlash.Pages
{
public class Test1Model : PageModel
{
public void OnGet()
{
if (Request.Path.Value == "") {
Response.Redirect("/myapp/");
}
}
}
}
アプリケーションのディレクトリ名をコードに記述したくない場合は、
~
を利用する下記のコードでも同じ動作にできます。
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace TrailingSlash.Pages
{
public class Test1Model : PageModel
{
public void OnGet()
{
if (Request.Path.Value == "") {
Response.Redirect(Url.Content("~/"));
}
}
}
}
実行結果
プロジェクトを実行し、
https://(ドメインルート)/myapp
のURLにアクセスすると、
https://(ドメインルート)/myapp/
にリダイレクトされ、末尾に "/" (トレイリングスラッシュ)が付いたURLになります。
対処法2 : asp-page 属性を利用する
asp-page 属性を利用してRazorPage名を指定してリンクを実行すると、URLの末尾の "/" に関係なく正しいURLに遷移できます。
コード
Main1.cshtmlのコードを下記に変更します。
@page "/"
@model TrailingSlash.Pages.Test1Model
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
}
<html>
<head>
</head>
<body>
<h2>テストです。</h2>
<a asp-page="/Sub1">リンク1</a><br />
</body>
</html>
解説
a
タグのリンクのhref 属性を使わずに asp-page 属性を利用します。属性の値には 遷移する RazorPage のページ名を指定します。
asp-page の動作の詳細については
こちらの記事も参照してください。
実行結果
プロジェクトを実行します。
https://(ドメインルート)/myapp
のURLにアクセスします。下図のページが表示されます。
[リンク1]をクリックします。
https://(ドメインルート)/myapp/sub/Sub1
に遷移し、Sub1のRazorPageが表示できます。
https://(ドメインルート)/myapp/
末尾に"/"を追加したURLにアクセスします。下図のページが表示されます。
[リンク1]をクリックします。
https://(ドメインルート)/myapp/sub/Sub1
に遷移し、Sub1のRazorPageが表示できます。
どちらのURLでもNotFoundにならずにページに遷移できることが確認できます。
対処法3 : asp-route 属性を利用する
asp-route 属性を利用してRazorPage名を指定してリンクを実行すると、URLの末尾の "/" に関係なく正しいURLに遷移できます。
コード
Startup.csとMain1.cshtmlのコードを下記に変更します。
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Routing;
namespace TrailingSlash
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "route001",
pattern: "/sub/Sub1");
endpoints.MapRazorPages();
});
}
}
}
@page "/"
@model TrailingSlash.Pages.Test1Model
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
}
<html>
<head>
</head>
<body>
<h2>テストです。</h2>
<a asp-route="route001">リンク1</a><br/>
</body>
</html>
解説
Starup.cs ファイルのUseEndpointsメソッド内に、名前付きのルーティングを作成します。ルーティングの追加は
MapControllerRoute()
メソッドを呼び出して追加します。
下記のコードでは、
route001
という名称のルーティングを追加しています。
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "route001",
pattern: "/sub/Sub1");
endpoints.MapRazorPages();
});
RazorPage側では、aタグのリンクをhref属性ではなく、asp-route 属性で記述します。asp-route属性の値にStartup.csファイルのコードで追加したルーティングの名称を指定しています。
<a asp-route="route001">リンク1</a><br/>
実行結果
プロジェクトを実行します。
https://(ドメインルート)/myapp
のURLにアクセスします。下図のページが表示されます。
[リンク1]をクリックします。
https://(ドメインルート)/myapp/sub/Sub1
に遷移し、Sub1のRazorPageが表示できます。
https://(ドメインルート)/myapp/
末尾に"/"を追加したURLにアクセスします。下図のページが表示されます。
[リンク1]をクリックします。
https://(ドメインルート)/myapp/sub/Sub1
に遷移し、Sub1のRazorPageが表示できます。
どちらのURLでもNotFoundにならずにページに遷移できることが確認できます。
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2021-10-27
作成日: 2021-10-26