vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

ASP.NET Core のアプリを Application Request Routing + IIS で動かしてみる

はじめに

相変わらずコロコロ話題が変わっていますが。。。お察しください。

タイトル通り、 ASP.NET Core で作ったアプリを IIS を使って動かしたい話です。

ASP.NET Core アプリの発行は以前挑戦しましたが、この時は Kestrel で動かしていただけでした。

ASP.NET Core アプリを IIS で実行する方法として、大きく 2 つあります。

  1. Application Request Routing を使う
  2. ASP.NET Core Module を使う

今回は Application Request Routing を試してみることにします。

Application Request Routing (以下 ARR)という IIS の Extension を使うことで、 IIS で指定した URL にアクセスがあった場合に、localhost:5000 で動かしている ASP.NET Core アプリにリダイレクトして表示することができるようになります(リバースプロキシ)。

f:id:mslGt:20190511021225p:plain

ASP.NET Core に組み込まれている Kestrel は、セキュリティなどの機能が IIS に比べて劣るとのことですが、この方法により外部からのアクセスは IIS で処理し、対象となる URL へのアクセスがあった場合のみ対応すれば良いことになります。

また ARR のインストール( + そのための Web Platform Installer (以下 Web PI )のインストール)は必要ですが、後述の self-contained application として発行していれば実行するサーバーマシンに .NET Core がインストールされていなくても動作させられたり、既存の環境をあまりいじらなくても良い、というのが利点だと思っています。

ASP.NET Core アプリの準備

ASP.NET Core アプリを作る

いつものように Empty テンプレートで ASP.NET Core プロジェクトを作成します。

で、 ASP.NET Core アプリ自体は localhost でのみアクセスできれば良いため、この状態で発行してしまいます。

self-contained application として発行する

self-contained application として発行することで、アプリを動作させるマシン上に .NET Core がインストールされていなくても動作させることが可能になります。

方法は .csproj ファイルに実行環境( Runtime )の情報を追加することと、発行時にそれを指定するだけです。

RunOnIisSample.csproj

< Project Sdk="Microsoft.NET.Sdk.Web">
    < PropertyGroup>
        < TargetFramework>netcoreapp2.2< /TargetFramework>
        < RuntimeIdentifiers>win-x86< /RuntimeIdentifiers>
    < /PropertyGroup>

    < ItemGroup>
        < PackageReference Include="Microsoft.AspNetCore.App" />
        < PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
    < /ItemGroup>

< /Project>
  • なおサンプルなどを見ると win10-x64 があるので win7-x86 としたらちゃんと動かなかったのも良い思い出(白目)。

発行

発行はいくつか方法があると思いますが、今回はコマンドで。

dotnet publish -c Release -r win-x86

これでプロジェクトの bin/Release/netcoreapp2.2/win-x86/publish 以下に、依存する dll ファイルとともに実行ファイルである RunOnIisSample.exe が出力されます。

IIS の準備

IIS Extension のインストール

IIS のインストール自体が完了していない場合は先にそちらを完了させてください。

まず ARR をインストールするのに必要な、 Web PI をインストールします。

インストール後、直接 Web PI を開くか、 IIS マネージャーから起動します。

検索で ARR を探し、インストールします( ver.3.0 を選びました)。

インストールが終わったら IIS マネージャーを再起動します(開いている場合)。

インストールした項目が追加されていれば完了です。

f:id:mslGt:20190511021319p:plain

IIS にアプリケーションを追加

まず URL 書き換えのベースとなる URL の登録を行います。

C:\inetpub\wwwroot に空のフォルダーを追加します。
※ 本来はアプリケーションのファイルを置くのですが、今回は URL を書き換えるためだけに用意するため、空で問題ありません。
(ただしもう少しちゃんとした方法はあるかも)

IIS マネージャーを起動し、左のツリーで サイト > Default Web Site を開きます
(最新の状態になっていれば、先ほどのフォルダーも表示されているはずです)。

Default Web Site 上で右クリック > アプリケーションの追加 から、アプリケーションを追加します。

f:id:mslGt:20190511021451p:plain

サイトの URL + エイリアス (今回は http://localhost:80/samplesite )でアクセスできるようになります。

物理パスは先ほどの空フォルダーを指定します。

f:id:mslGt:20190511021536j:plain

これで http://localhost:80/samplesite という URL が使用可能になるわけですが、中身は空であるためアクセスしてもエラーになります。

Proxy の有効化

左のツリーのトップ(今回は DESKTOP-3I579DO~ )をクリックして ARR を開き、右のメニューから Proxy > Server Proxy Settings... を開き、 Enable proxy にチェックを入れて有効化します。

余談ですが、下の Proxy Type で、 Use URL Rewrite to inspect incomming request にチェックを入れて Reverse proxy に URL を設定すると、そのサイトに登録されている全 URL が Reverse proxy で指定した URL に飛ばされます。

便利な場合もあるとは思いますが、今回は個別に設定したいためチェックは入れずに置いておきます(なぜ説明した)。

URL 書き換え

もう一度作成した samplesite をクリックし、 URL書き換え を開きます。

右のメニューから 規則の追加 を起動し、 受信規則と送信規則 > リバースプロキシ を選択します。

受信規則のホストとして、 ASP.NET Core アプリで指定している localhost:5000 を設定します。

f:id:mslGt:20190511021606j:plain

この状態で ASP.NET Core アプリを起動し、 http://localhost:80/samplesite にアクセスすると、 http://localhost:5000 を開いた時と同じものが表示されます。

今回 IISlocalhost でアクセスしているせいであまりありがたみは感じられないのですが、これで IIS を外部公開しさえすれば ASP.NET Core アプリを外部から表示することができるようになります。

Static File を読み込む

JavaScriptCSS などの Static File を wwwwroot に置き、 IIS を使ってページを開いたところ、正しく読み込めませんでした。

という話をしたいのですが、 http://localhost:80/samplesite で開いたところ読み込めてしまったため、先に hosts ファイルを使って localhost から masanori.example.jp というドメインに変更してみます。

hosts ファイルによるドメイン変更

C:\Windows\System32\drivers\etc にある hosts ファイルを管理者として開き、下記を追加します。

hosts

~省略~
{マシンのIPアドレス} masanori.example.jp
~省略~

ドメイン名はお好みで変更してください。

で、 IIS マネージャーで masanori.example.jp というサイトを作り、先ほどと同じ手順でアプリケーションを追加します。

f:id:mslGt:20190511021938j:plain

内容がかぶらなければ先ほどのアプリケーションは放置で問題ありませんが、使わないので消してもらってもよいと思います。

物理パスは同じフォルダーを指定してやれば、 URL の書き換え設定はそのまま有効になっていると思います。

http://masanori.example.jp:8088/samplesite2 のような URL で、 ASP.NET Core のページが表示されれば OK です。

Static Files のパスが合わない

(バグを再現する)準備が整ったところで、下記のファイルを追加していきます。

  • RunOnIisSample
    • Controllers
      • HomeController.cs
    • Views
      • Shared
        • _Layout.cshtml
      • _ViewStart.cshtml
      • Index.cshtml
    • wwwroot

HomeController.cs

using Microsoft.AspNetCore.Mvc;

namespace RunOnIisSample.Controllers
{
    public class HomeController: Controller
    {
        [Route("")]
        public IActionResult Index()
        {
            return View("/Views/Index.cshtml");
        }
    }
}

_Layout.cshtml

< !DOCTYPE html>
< html lang="ja">
< head>
    < meta charset="utf-8" />
    < title>@ViewData["title"]< /title>
    < link rel="stylesheet" type="text/css" href="src/css/site.css"/>
< /head>
< body>
    @RenderBody()
< script src="src/js/site.js">< /script>
< /body>
< /html>

_ViewStart.cshtml

@{
    Layout = "_Layout";
}

Index.cshtml

@{
    ViewData["title"] = "Home"; 
}

site.js

alert("hello");

site.css

body{
    background-color: aqua;
}

特筆すべきこともないというか、ほとんど空っぽの状態です。

localhost:5000 でページの色が変わること、アラートが表示されることは確認しておきます。

問題を確認

んで、 http://masanori.example.jp:8088/samplesite2 を開くと、 JavaScriptCSS も読み込まれていません。

Firefox の開発者ツールで確認したところ、 JavaScript について下記のようなエラーが出ていました。

http://masanori.example.jp:8088/src/js/site.js” からのスクリプトが読み込まれました。しかし、この MIME タイプ (“text/html”) は正しい JavaScriptMIME タイプではありません。[詳細] samplesite2
< script> のソース “http://masanori.example.jp:8088/src/js/site.js” の読み込みに失敗しました。

最初 MIME タイプの内容に気を取られてしまい、以前試したように Controller からファイルを返してみる、といったことも試していました。

が、原因は何のことはない、 URL が違うから、ということです orz

「samplesite2」の部分が抜けてしまっているために、 404 エラーが発生していました、と。

なぜ http://localhost:80/samplesite だと問題なく、 http://masanori.example.jp:8088/samplesite2 だとエラーになるのかは不明ですが、とにかく何とかしてみることにします。

IHostingEnvironment.EnvironmentName で切り分け

例えば実行環境が Development だった場合は ~/src/js/site.js を、それ以外は ~/samplesite2/src/js/site.js を開くようにします。

※根本的な解決ではない

HomeController.cs

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;

namespace RunOnIisSample.Controllers
{
    private readonly string _additionalPath;

        public HomeController(IHostingEnvironment env)
        {
            _additionalPath = (env.EnvironmentName == "Development") ? "" : "samplesite2/";
        }
        
        [Route("")]
        public IActionResult Index()
        {
            ViewData["additional_path"] = _additionalPath;
            return View("/Views/Index.cshtml");
        }
    }
}
  • コンストラクターで ViewData に値を渡していないのは、そのタイミングで値を入れても View 側で受け取れなかったためです。読み込む前にリセットされるのかもしれません。

_Layout.cshtml

< !DOCTYPE html>
< html lang="ja">
< head>
    < meta charset="utf-8" />
    < title>@ViewData["title"]< /title>
    < link rel="stylesheet" type="text/css" href=@ViewData["additional_path"]"src/css/site.css"/>
< /head>
< body>
    @RenderBody()
< script src=@ViewData["additional_path"]"src/js/site.js">< /script>
< /body>
< /html>

一つ一つ設定していくのは面倒なので、一括で処理したいところではあるのですが。。。

次回に続く。。。かもしれない。

参照

IIS

ASP.NET Core

hosts