フロントエンドをAngular等のSPAフレームワーク、バックエンドをASP.NET Coreという環境を作るためのTipsです。
実はこれから紹介することをしなくてもはじめからがっちゃんこされた状態のものが配布されています。
が、中をよく見てもイマイチよくわからなかったので自分でがっちゃんこさせてみました。
とりあえず先にそれくれよ
やりたいこと
- サーバーは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を動かすためにいくつか必要なライブラリを追加します。
<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の編集
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は変えずにとりあえずHome
ControllerのIndex
メソッドを実行するという設定です。
つまりHomeController
のIndex
メソッドにAngularのindex.htmlを返すようにすれば全てのルーティングが上手くいくという設計になっているのです👏
HomeController.csの追加
既にControllers
フォルダがあると思いますが、そのフォルダに新しく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へのルーティングを追加しましょう。
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にアクセスすることもできます。