ASP.NET Coreで作成したWebアプリケーションをサーバー上で公開する場合、リンクがうまく働かない問題があります。サーバーの設定を変えれば対応できるのかもしれませんが、レンタルサーバーの場合は管理者権限がないのでどうすることもできません。
1 2 3 |
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> | <a asp-action="Details" asp-route-id="@item.Id">Details</a> | <a asp-action="Delete" asp-route-id="@item.Id">Delete</a> |
このようにcshtmlに書くと以下のように変換されます。
1 2 3 |
<a href="/Movies/Edit/2">Edit</a> | <a href="/Movies/Details/2">Details</a> | <a href="/Movies/Delete/2">Delete</a> |
サブディレクトリではなくドメインやサブドメインで公開したい場合はなんの問題もありませんが、サブディレクトリで運用する場合は困ります。だからといってリンクの部分を手動で書き換えるのも大変です。
タグ ヘルパーは自分で定義することができます。だったら再定義することはできるのか? やってみるとうまくいったのでその方法を公開します。
まずこんなクラスをつくります。もしhttps://lets-csharp.com/samples/2207/first-mvcでアプリを公開したいのであれば最後のスラッシュを取ったものをGlobal.BaseUrlプロパティで取得できるようにします。クラス名とプロパティ名はほかと重複しなければなんでもかまいません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class Global { public static bool IsRelease { get { return false; } } public static string BaseUrl { get { if(IsRelease) return "https://lets-csharp.com/samples/2207/first-mvc"; else return ""; } } } |
次にaタグやlinkタグ、imgタグ、scriptタグ、formタグのタグヘルパーを再定義します。また新たにemailタグのタグヘルパーを定義します。
aタグの場合、まずhref属性があるかどうかを調べます。あれば直接 <a href=”#”>アンカーテキスト</a> と書かれている場合は変換されないようにするためにoutput.Attributes[“href”].Valueで取得されたものがstring型かどうかを調べます。string型の場合はGlobal.BaseUrlを先頭につけて再セットします。linkタグの場合も同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class ATagHelper : TagHelper { public override void Process(TagHelperContext context, TagHelperOutput output) { if (output.Attributes.ContainsName("href")) { object o = output.Attributes["href"].Value; if (o.GetType() == typeof(string)) { string s = (string)o; output.Attributes.SetAttribute("href", Global.BaseUrl + s); } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class LinkTagHelper : TagHelper { public override void Process(TagHelperContext context, TagHelperOutput output) { if (output.Attributes.ContainsName("href")) { object o = output.Attributes["href"].Value; if (o.GetType() == typeof(string)) { string s = (string)o; output.Attributes.SetAttribute("href", Global.BaseUrl + s); } } } } |
imgタグの場合、まずsrc属性があるかどうかを調べます。あれば直接 <img src=”#” /> と書かれている場合は変換されないようにするためにoutput.Attributes[“src”].Valueで取得されたものがstring型かどうかを調べます。string型の場合はGlobal.BaseUrlを先頭につけて再セットします。scriptタグの場合も同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class ImgTagHelper : TagHelper { public override void Process(TagHelperContext context, TagHelperOutput output) { if (output.Attributes.ContainsName("src")) { object o = output.Attributes["src"].Value; if (o.GetType() == typeof(string)) { string s = (string)o; output.Attributes.SetAttribute("src", Global.BaseUrl + s); } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class ScriptTagHelper : TagHelper { public override void Process(TagHelperContext context, TagHelperOutput output) { if (output.Attributes.ContainsName("src")) { object o = output.Attributes["src"].Value; if (o.GetType() == typeof(string)) { string s = (string)o; output.Attributes.SetAttribute("src", Global.BaseUrl + s); } } } } |
formタグの場合、どこにpostするのかをaction属性をつかって指定するのですが、この部分を書き換えます。まずaction属性があるかどうかを調べます。ある場合はstring型かどうかを調べて、string型の場合はGlobal.BaseUrlを先頭につけて再セットします。
1 2 3 4 5 6 7 8 |
public class FormTagHelper : TagHelper { public override void Process(TagHelperContext context, TagHelperOutput output) { string s = (string)output.Attributes["action"].Value; output.Attributes.SetAttribute("action", Global.BaseUrl + s); } } |
emailタグのタグヘルパーを定義します。
1 2 3 4 5 6 7 8 9 10 11 |
public class EmailTagHelper : TagHelper { public string MailTo { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output) { output.TagName = "a"; // <email>が<a>に置換される output.Attributes.SetAttribute("href", "mailto:" + MailTo); output.Content.SetContent(MailTo); } } |
それからタグヘルパーが機能するようにするには_ViewImports.cshtmlを以下のように編集します。
Views\_ViewImports.cshtml
1 2 3 4 5 6 7 |
@using WebApplication1 @using WebApplication1.Models @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers // 上3行は既存の行。追加するのはこの1行だけ // カンマのあとのWebApplication1はアセンブリの名前と一致させること @addTagHelper *, WebApplication1 |
これで以下のように変換されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<!-- 変換前 --> <a asp-area="" asp-controller="Movies" asp-action="Index">映画のデータ管理アプリ</a> <!-- 変換後 --> <a href="https://lets-csharp.com/samples/2207/first-mvc/Movies">映画のデータ管理アプリ</a> <!-- 変換前 --> <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" asp-append-version="false" /> <!-- 変換後 --> <link rel="stylesheet" href="https://lets-csharp.com/samples/2207/first-mvc/lib/bootstrap/dist/css/bootstrap.min.css" /> <!-- 変換前 --> <img src="/images/hato.png" asp-append-version="false" /> <!-- 変換後 --> <img src="https://lets-csharp.com/samples/2207/first-mvc/images/hato.png" /> <!-- 変換前 --> <script src="~/lib/jquery/dist/jquery.min.js" asp-append-version="false"></script> <!-- 変換後 --> <script src="https://lets-csharp.com/samples/2207/first-mvc/lib/jquery/dist/jquery.min.js"></script> <!-- 変換前 --> <form asp-action="Create"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="Title" class="control-label"></label> <input asp-for="Title" class="form-control" /> <span asp-validation-for="Title" class="text-danger"></span> </div> <div class="form-group"> <input type="submit" value="Create" class="btn btn-primary" /> </div> </form> <!-- 変換後 --> <form action="https://lets-csharp.com/samples/2207/first-mvc/Movies/Create" method="post"> <div class="form-group"> <label class="control-label" for="Title">Title</label> <input class="form-control" type="text" data-val="true" data-val-required="The Title field is required." id="Title" name="Title" value="" /> <span class="text-danger field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span> </div> <div class="form-group"> <input type="submit" value="Create" class="btn btn-primary" /> </div> <input name="__RequestVerificationToken" type="hidden" value="CfDJ8J6sSSFfzXNEqilV8r7o_CXx3LbgV1CZTPBzCn2ENQybShxFImrc2I8fSE5p-p_U_NVzw-VhJI-PjG00H8VGsPMXFPKMeZ3uVot-zY-tE1eYfRmyjy7YyLirKpikSh7iNwEPKzJVpWMGpec5m_kQbBY" /> </form> |