Help us understand the problem. What is going on with this article?

ASP.NET Core上でSPAを動かしたい

More than 3 years have passed since last update.

フロントエンドをAngular等のSPAフレームワーク、バックエンドをASP.NET Coreという環境を作るためのTipsです。

実はこれから紹介することをしなくてもはじめからがっちゃんこされた状態のものが配布されています。
が、中をよく見てもイマイチよくわからなかったので自分でがっちゃんこさせてみました。

とりあえず先にそれくれよ

https://blogs.msdn.microsoft.com/webdev/2017/02/14/building-single-page-applications-on-asp-net-core-with-javascriptservices/

やりたいこと

  • サーバーはASP.NET Coreで使っているKestrelを使いたい
  • @angular/cliを使ってビルドされたものをフロントとして使いたい
  • AngularのRouting機能を使ってページ遷移したい

環境

  • dotnet cli 1.1.0
  • @angular/cli 1.0.0

ASP.NET Coreプロジェクトの作成

Visual Studioから簡単に作れるのですが、ここではコマンドで作ってみます。

mkdir server
cd server
dotnet new webapi
dotnet restore

ライブラリ追加

SPAを動かすためにいくつか必要なライブラリを追加します。

server.csproj.diff
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
+    <PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="1.1.0" />
+    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
  </ItemGroup>

</Project>

csprojを編集したあと再びdotnet restoreコマンドを打ちます。

Startup.csの編集

Startup.cs.diff
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
     loggerFactory.AddConsole(Configuration.GetSection("Logging"));
     loggerFactory.AddDebug();

+    app.UseStaticFiles();

-    app.UseMvc();
+    app.UseMvc(routes =>
+    {
+        routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
+        routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" });
+    });
}

以前の自分の記事「Slackで特定のキーワードに反応して自動的にReactionをつけるBotを作った」でも書きましたが、app.UseXXXには呼び出す順番があります。ひとまずは上の順番の通りにしてください。

ポイントはUseMvcの中でやっていることです。ここでルーティングを設定しています。

まず、MapRouteではASP.NET Core MVCのControllerにアクセスするためのルーティング設定です。URLでルートパスを指定されたときはHomeと名のついたControllerのIndexメソッドを実行するという設定になっています。なので、この後にHomeControllerを作成します。
次にMapSpaFallbackRouteでもHomeControllerのIndexを指定しています。両方同じことをやっているように見えますが、実はこれがSPAで重要な設定になります。

MapSpaFallbackRouteの役割

まずAngular側でもルーティングの仕組みがあります。URLも/page1のように書き換わります。しかし実際はindex.htmlの中でjavascriptをうまく使いあたかもそのURLに遷移しているかのように見せているだけです。(だからSPAなんですけどね)
では一方でそのURLにそのままアクセスしてみるとどうでしょうか。Angularはindex.htmlしかありません。なのに/page1とかにアクセスされてもpage1/index.htmlはありません。なのでこの場合404を返してしまいます。
それを防ぐのがMapSpaFallbackRouteの役割です。これは存在しないパスを指定されたときに、URLは変えずにとりあえずHomeControllerのIndexメソッドを実行するという設定です。

つまりHomeControllerIndexメソッドにAngularのindex.htmlを返すようにすれば全てのルーティングが上手くいくという設計になっているのです👏

HomeController.csの追加

既にControllersフォルダがあると思いますが、そのフォルダに新しくHomeController.csを追加します。

HomeController.cs
using Microsoft.AspNetCore.Mvc;

namespace server.Controllers
{
    /// <summary>
    /// Default MVC Controller
    /// </summary>
    public class HomeController : Controller
    {
        /// <summary>
        /// Open the wwwroot/index.html
        /// </summary>
        public IActionResult Index() => File("/index.html", "text/html");
    }
}

namespaceは自分の環境に合わせてください。
ここでやっていることは単純にwwwroot/index.htmlをレスポンスとして返す処理をしています。あとはwwwrootフォルダにAngularを置けば良いということですね。もしwwwrootフォルダがなければcsprojのあるフォルダから新しくwwwrootという名前でフォルダを作っておきましょう。

これでASP.NET Core側は完了です。

Angularプロジェクトの作成

次にAngular側を作っていきましょう。作業フォルダはどこでもOKです。

ng new --routing client
cd client
ng generator component page1

clientの部分がアプリ名になるので好きなものに変更します。
ルーティング用に新たにpage1というコンポーネントを作成しています。

app-routing.module.tsの編集

--routing オプションをつけているとappフォルダにこのファイルが作成されます。ここにpage1へのルーティングを追加しましょう。

app-routing.module.ts.diff
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

+ import { Page1Component } from './page1/page1.component';

const routes: Routes = [
  {
+    path: 'page1',
+    component: Page1Component
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

これで終わりです。

Publish

ng build --prod

distフォルダの中に作ったフロント一式が入ってますので、これをASP.NET Coreのwwwrootフォルダにコピーします。

実行

ASP.NET Coreのフォルダにcdして

dotnet run

でサーバーが起動します。http://localhost:5000/でブラウザにアクセスするとAngularのページが表示され、http://localhost:5000/page1にアクセスしても404にならずページが表示されていると思います。
また、http://localhost:5000/api/valuesにアクセスするとJSONがとれますが、これはASP.NET CoreのValuesControllerがリクエストを返しています。もちろんAngularからAPIにアクセスすることもできます。

kuluna
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away