アプリケーションルートがドメインルートにない場合のリンクの動作と実装方法を紹介します。
アプリケーションのルートがサーバーのルートでない場合、アプリケーションルートの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を 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 のページが表示されます。
プロジェクトを実行し、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を 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が正しいリンク先に遷移できるようになります。
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になります。
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にならずにページに遷移できることが確認できます。
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にならずにページに遷移できることが確認できます。