.NET 6 ASP.NET Core Web API、
React、Elastic APM でシンプルなアプリを
構築してみよう
鈴⽊ 章太郎
Elastic テクニカルプロダクトマーケティングマネージャー/エバンジェリスト
デジタル庁 省庁業務グループ ソリューションアーキテクト
Elastic
Technical Product Marketing
Manager/Evangelist
デジタル庁
省庁業務グループ
ソリューションアーキテクト
元 Microsoft Technical Evangelist
Twitter : @shosuz
Shotaro Suzuki
l API プロジェクトの作成
l Entity Framework Core コード作成と最初の Database ⽣成
l API エンドポイント⽣成
l React プロジェクトの作成
l Azure App Service にサーバを公開
l Azure Static Web App + GitHub Actions に React を公開
l CORS ポリシーの修正
l Elastic APM によるアプリケーションの監視
l まとめ
アジェンダ
今回は
Visual Studio 2022 for Mac Preview で
デモアプリを作成
https://visualstudio.microsoft.com/ja/vs/mac/preview/
加えて、
Azure Data Explorer、
GitHub Desktop for mac、
Visual Studio Code
今回のデモアプリのイメージ
Azure
SQL Database
Elastic Cloud
東⽇本リージョン
マスターノード x 1
データノード x 2
ML ノード x 1
https://f79...c67.japaneast
.azure.elastic-
cloud.com:9243/
全⽂検索クエリ
CRUD
検索・更新 UI
Azure サブスクリプション
Visual
Studio
2022 for
Mac
Azure
App Service
Elastic APM
Endpoint に送信
Azure Data Explorer
ASP.NET 6 Web API
Elastic APM Agent
iOS / Android
Mobile App
React
Native
Elastic
APM
Agent
React Web App
Azure
Static Web Apps
React Web App
API プロジェクトの作成
Lightweight, single-file, cloud native APIs
Low ceremony, top-level C# programs
Easy to get started
Path to MVC
.NET 6 Minimal APIs for Cloud Native Apps
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
Minimal code for minimal apps
• C#のトップレベルプログラムをベースに最⼩限のコードで ASP.NET Web API を構築する軽量な⽅法
• 従来のコントローラースタイルの Web API のようなコードなしにクラウドスケール API を簡単に構築可能
• プロジェクトが⼤きくなり始めたら、これらを ASP.NET MVC コントローラに移⾏することも可能
• パイプライン処理や単純な CRUD のような複雑さを必要としない API を迅速に構築するための選択肢
ASP.NET Core Web API
ASP.NET Core Web API
Minimal API を使うので、真ん中のチェックは外す)
ASP.NET Core Web API
プロジェクト名は、aspnetserver (ソリューション名は任意)にする
ASP.NET Core Web API
この状態でデバッグ実⾏
ASP.NET Core Web API
この状態でデバッグ実⾏
不要なコードを削除 – Program.cs (Startup.cs は廃⽌)
• WeatherForecast 関連のソースコードを全て除去し保存
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.Run();
Entity Framework Core コード作成と
最初の Database ⽣成
Data フォルダーの作成と Sealed Class 作成
Post クラスを Sealed で作成し継承できないようにする
Post Class
• attribute として System.ComponentModel.DataAnnotations を追加し、クラスを作成する
using System.ComponentModel.DataAnnotations;
namespace aspnetserver.Data
{
internal sealed class Post
{
[Key]
public int PostId { get; set; }
[Required]
[MaxLength(100)]
public string Title { get; set; } = string.Empty;
[Required]
[MaxLength(100000)]
public string Content { get; set; } = string.Empty;
}
}
NuGet Package 追加
• NuGet Package Manager 起動
• EntityFrameworkCore
• EntityFrameworkCore.Design
• EntityFrameworkCore.SQLServer
を追加
• Entity Framework Core CLI ツールインストール
dotnet tool install --global dotnet-ef
AppDbContext 作成
• Azure SQL Database 上に AppDB を⽣成しつつ Seed を作成
using Microsoft.EntityFrameworkCore;
namespace aspnetserver.Data
{
internal sealed class AppDBContext : DbContext
{
public DbSet<Post> Posts {get; set;}
protected override void OnConfiguring(DbContextOptionsBuilder dbContextOptionsBuilder)
=> dbContextOptionsBuilder.UseSqlServer("Server=tcp:spaappdev.database.windows.net,1433;
Initial Catalog=AppDb;Persist Security Info=False;UserID=shotaro;Password=(password)#;
MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
Post[] postsToSeed = new Post[6];
for (int i = 1; i <= 6; i++)
{
postsToSeed[i - 1] = new Post
{
PostId = i,
Title = $"Post {i}",
Content = $"これは投稿{i}で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している."
};
}
modelBuilder.Entity<Post>().HasData(postsToSeed);
}
}
}
dotnet ef migrations add
dotnet ef migrations add 1stMigration -o "Data/Migrations"
Entity Framework による DB 作成とサンプルデータ確認
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace aspnetserver.Data.Migrations
{
public partial class _1stMigration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Posts",
columns: table => new
{
PostId = table.Column<int>(type: "int", nullable: false).Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Content = table.Column<string>(type: "nvarchar(max)", maxLength: 100000, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Posts", x => x.PostId);
});
migrationBuilder.InsertData(
table: "Posts",
columns: new[] { "PostId", "Content", "Title" },
values: new object[,]
{
{ 1, "これは投稿 1 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 1" },
{ 2, "これは投稿 2 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 2" },
{ 3, "これは投稿 3 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 3" },
{ 4, "これは投稿 4 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 4" },
{ 5, "これは投稿 5 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 5" },
{ 6, "これは投稿 6 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 6" }
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Posts");
}
}
}
dotnet ef database update
dotnet ef database update
Azure Data Studio
DB操作のためのレポジトリ (PostsRepositry.cs) 作成
using Microsoft.EntityFrameworkCore;
namespace aspnetserver.Data
{
internal static class PostsRepository
{
internal async static Task<List<Post>> GetPostsAsync()
{
using (var db = new AppDBContext())
{
return await db.Posts.ToListAsync();
}
}
internal async static Task<Post> GetPostByIdAsync(int postId)
{
using (var db = new AppDBContext())
{
return await db.Posts
.FirstOrDefaultAsync(post => post.PostId == postId);
}
}
internal async static Task<bool> CreatePostAsync(Post postToCreate)
{
using (var db = new AppDBContext())
{
try
{
await db.Posts.AddAsync(postToCreate);
return await db.SaveChangesAsync() >= 1;
}
catch (Exception e)
{
return false;
}
}
}
internal async static Task<bool> UpdatePostAsync(Post postToUpdate)
{
using (var db = new AppDBContext())
{
try
{
db.Posts.Update(postToUpdate);
return await db.SaveChangesAsync() >= 1;
}
catch (Exception e)
{
return false;
}
}
}
internal async static Task<bool> DeletePostAsync(int postId)
{
using (var db = new AppDBContext())
{
try
{
Post postToDelete = await GetPostByIdAsync(postId);
db.Remove(postToDelete);
return await db.SaveChangesAsync() >= 1;
}
catch (Exception e)
{
return false;
}
}
}
}
}
API エンドポイント⽣成
Program.cs 修正 – MapGet 追加(全て)
• using aspnetserver.Data を追加し、クラスを作成する
using aspnetserver.Data;
---
app.MapGet("/get-all-posts", async () => await PostsRepository.GetPostsAsync())
.WithTags("Posts Endpoints");
app.Run();
Program.cs 修正 – Swagger 修正
• SwaggerDoc を作成する
• Swagger UI を開発環境以外でも 表⽰するように書き換える
• launchSettings.json の "launchUrl": "swagger" ⾏をコメントアウトする
理由︓HTTP Local Host のみ(起動 URL なし)でアプリが起動し、次にポート番号なしで Swagger が起動するため
using aspnetserver.Data;
---
builder.Services.AddSwaggerGen(swaggerGenOptions =>
{
swaggerGenOptions.SwaggerDoc("v1", new OpenApiInfo
{ Title = "ASP.NET Core 6 Web API React Sample", Version = "v1" });
});
app.UseSwagger();
app.UseSwaggerUI(swaggerUIOptions =>
{
swaggerUIOptions.DocumentTitle = "ASP.NET Core 6 Web API React Sample";
swaggerUIOptions.SwaggerEndpoint("/swagger/v1/swagger.json",
"超シンプルな Post モデルを提供する Web API.");
swaggerUIOptions.RoutePrefix = string.Empty;
});
Program.cs 修正 – MapGet(postId) 追加
• MapGet(postId)、MapPost、MapPut、MapDelete
app.MapGet("/get-post-by-id/{postId}", async (int postId) =>
{
Post postToReturn = await PostsRepository.GetPostByIdAsync(postId);
if (postToReturn != null)
{
return Results.Ok(postToReturn);
}
else
{
return Results.BadRequest();
}
}).WithTags("Posts Endpoints");
Program.cs 修正 – MapPost 追加
• MapGet(postId)、MapPost、MapPut、MapDelete
app.MapPost("/create-post", async (Post postToCreate) =>
{
bool createSuccessful = await PostsRepository.CreatePostAsync(postToCreate);
if (createSuccessful)
{
return Results.Ok("作成しました︕");
}
else
{
return Results.BadRequest();
}
}).WithTags("Posts Endpoints");
Program.cs 修正 – MapPut 追加
• MapGet(postId)、MapPost、MapPut、MapDelete
app.MapPut("/update-post", async (Post postToUpdate) =>
{
bool updateSuccessful = await PostsRepository.UpdatePostAsync(postToUpdate);
if (updateSuccessful)
{
return Results.Ok("更新しました︕");
}
else
{
return Results.BadRequest();
}
}).WithTags("Posts Endpoints");
Program.cs 修正 – MapDelete 追加
• MapGet(postId)、MapPost、MapPut、MapDelete
app.MapDelete("/delete-post-by-id/{postId}", async (int postId) =>
{
bool deleteSuccessful = await PostsRepository.DeletePostAsync(postId);
if (deleteSuccessful)
{
return Results.Ok("削除しました︕");
}
else
{
return Results.BadRequest();
}
}).WithTags("Posts Endpoints");
実⾏結果確認
• MapGet(postId)、MapPost、MapPut、MapDelete が表⽰され、試せる
React プロジェクトの作成
React シングルページアプリケーションの作成
• Terminal でルートフォルダに移動
• コマンドでフォルダと React プロジェクトを⽣成する
• React ディレクトリに移動し git 関連を削除(全体で作成するため)し、VSCode を起動
npx create-react-app reactclient
cd reactclient
npm start
sudo rm -r.git
code ./
Visual Studio Code の起動と設定
• Terminal を開き下記のコマンドを打つ
• 実⾏ (port 3000)
npm install --save -cross-env
npm start
Visual Studio Code の拡張機能追加とファイルの整理
• 拡張機能を追加(任意)
• Bracket Pair Colorizer 2 v0.2.2
• Prettier
• VS Code ES7
React/Redux/React-Native/JS
snippets
• Material Icon Theme
• 不要なファイルの削除
• setupTests.js
• reportWebVitals.js
• logo.svg
• index.css
• App.Test.js
• App.css
• Index.js の編集
• 制限モード削除
ReactDOM.render(<App />,document.getElementById('root'));
• reportWebVitals();、import reportWebVitals from ʻ./reportWebVitalsʻ;、import './index.css'; 削除
CORS ポリシーの追加
• Server – Program.cs
builder.Services.AddCors(options =>
{
options.AddPolicy("CORSPolicy",
builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("http://localhost:3000",
"https://appname.azurestaticapps.net/");
});
});
React アプリで実装する内容と実⾏結果
• Bootstrap の追加
• 投稿をテーブルで表⽰
• サーバープロジェクト側の
CORS ポリシー追加
• テーブルにデータを表⽰
• API エンドポイント付き定数
ファイル
• データ新規追加フォーム
コンポーネントの作成
• データ更新フォーム
コンポーネントの作成
• データの削除
Azure App Service にサーバを公開
Visual Studio 2022 for mac で Azure に公開
• Wizard を使⽤してホスティングプランは F1- Free に変更しながら deploy
https://aspnet6webapireactsample.azurewebsites.net/index.html
Azure Static Web App +
GitHub Actions で React を公開
• 静的サイトに最適化されたホスティング環境
• CI/CD を GitHub Actions と統合
• Azure Functions によるサーバーレス API の統合
Microsoft Azure
www
Azure Static Web Apps
• GitHub 統合
• 統合 API サポート
• Web アプリケーションの構築
• 静的サイトの発⾏
• Web アプリケーションのデプロイ
• 認証プロバイダーの統合
• Azure Active Directory、Facebook、
Google、GitHub、 Twitter
Azure Static Web Apps の機能
Web API + リッチな Web UI を実現する機能を提供
https:/ / . . .
Blazor
+
https:/ / . . .
+
JS
JavaScript / C# による静的 Web サイトの開発・ホスト
Azure Static Web App と
GitHub Actions ワークフロー
• Azure Static Web Apps では、必要なすべての Azure リソースのプロビジョニングが
⾃動的に実施
• アプリをホストする前、変更を⾏う際、アプリをビルドするためのリポジトリへのコミットま
たは pull request によって⾏う必要あり
• Azure Static Web Apps の主な機能としてアプリケーションをビルドして発⾏するため
の GitHub Actions ワークフローを設定できる
• Azure Static Web Apps リソースを作成すると、GitHub Actions ワークフローが
作成される
• ワークフローはすぐにトリガーされ、アプリのビルドと発⾏が⾏われる
• リポジトリ内の監視対象ブランチに変更を加えるたびに、ワークフローがトリガーされる
Azure Static Web Apps
2つの⾃動化された側⾯
1.アプリを構成する基になる Azure リソースのプロビジョニング
2.アプリケーションをビルドして発⾏する GitHub Actions ワークフロー
• Azure Static Web Apps で Web にアプリを発⾏すると、
Web アプリとスケーラブルな API のホストが⾼速になる
• GitHub Actions によって提供される統合ビルドおよびデプロイ
ワークフローが得られる
Static Web Apps インスタンスを GitHub に接続
• Static Web Apps インスタンスを作成する
には、GitHub にサインインし、アプリのコード
が含まれているリポジトリを指定
• アプリを⾃動的にビルドしてデプロイできるよう
に、リポジトリ内に3つのフォルダーパスを指定
• アプリのビルド出⼒はアプリケーションのビルド
出⼒ディレクトリへの相対パス
• 例︓ビルドされた資産を my-app/dist フォル
ダーに出⼒するアプリが my-app にある場合、
この場所には dist を指定
• ビルドの詳細画⾯でビルドのプリセットを選択
• React を選択すると⾃動的に
• アプリの場所
• アプリのビルド出⼒の場所
• API の場所
の3つに値が⼊⼒される
今回は / 直下ではなくて
/reactclient にしないと動かない
ので注意︕
GitHub Actions を使⽤してソースコードから静的資産へ
• GitHub リポジトリにはソース コードが格納され、発⾏する
前にビルドする必要あり
• Static Web Apps インスタンスを作成すると、⾃動的にリ
ポジトリに GitHub Actions ワークフローが作成される
• このワークフローによって、追跡するように選択したブランチに
対して変更内容をプッシュしたり、pull request を作成し
たりするたびに、アプリがビルドされる。これにより、ソースコー
ドが静的資産に変換され、Azure によって提供される
• ビルドが完了すると、アクションによって資産がデプロイされる
• その GitHub アクションはリポジトリ
の .github/workflows フォルダーに追加される
• 必要に応じて、このファイルを確認または変更できる
• リソースの作成時に⼊⼒した設定は、GitHub アクションの
ファイルに保存される
Azure Functions と統合された API
• API が必要な場合
• Azure Functions プロジェクトとしてリポジトリ
内に実装できる
• API は⾃動的にデプロイされ、Static Web
Apps インスタンスによってホストされる
• GitHub Actions ワークフローによって、指定
したフォルダーの名前でリポジトリ内の API が
検索される
• 通常、API アプリは api または functions と
いう名前のフォルダーに配置されるところ、好きな
名前を付けることができる
• 指定したフォルダー内に API が検出され
ない場合
• API は発⾏されず、アプリのみ発⾏される
例︓
https://docs.microsoft.com/ja-jp/learn/modules/publish-static-web-app-api-preview-url/3-build-api
GitHub Actions を使⽤してソースコードから静的資産へ
• GitHub リポジトリの [Actions](アクション) ページに移動し、ビルドおよびデプロイアクションの
状態を確認できる
• Visual Studio Code エディターで 例︓my-first-static-web-app を右クリックし[ポータルで開く]
• [Azure Static Web Apps CI/CD] を選択
• ci: add Azure Static Web Apps workflow file のようなタイトルのついた最上位のコミットを選択
• 左にある [Build and Deploy Job]リンクを選択。ここから、ビルド時にアプリの進⾏状況を確認できる
Web サイトを表⽰する
• GitHub アクションによって Web アプリのビルドと発⾏が完了すると実⾏中のアプリを参照して
確認できる
• Azure portal の [URL] リンクを選択して、ブラウザーでアプリにアクセス
CORS ポリシーの修正
CORS ポリシーの修正
• Server – Program.cs
• Client – Constants.js
builder.Services.AddCors(options =>
{
options.AddPolicy("CORSPolicy",
builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("http://localhost:3000",
"https://pine-tree-04bf7ae98.2.azurestaticapps.net/");
});
});
Azure Static Web Apps に
デプロイ後に決まるので最初は
~appname.azurestaticapps.
net ~ 等にしておく︕
const API_BASE_URL_DEVELOPMENT = 'https://localhost:7293';
const API_BASE_URL_PRODUCTION = 'https://net6webapireactsample.azurewebsites.net/';
Elastic APM によるアプリケーションの監視
今回のデモアプリのイメージ
Azure
SQL Database
Elastic Cloud
東⽇本リージョン
マスターノード x 1
データノード x 2
ML ノード x 1
https://f79...c67.japaneast
.azure.elastic-
cloud.com:9243/
全⽂検索クエリ
CRUD
検索・更新 UI
Azure サブスクリプション
Visual
Studio
2022 for
Mac
Azure
App Service
Elastic APM
Endpoint に送信
Azure Data Explorer
ASP.NET 6 Web API
Elastic APM Agent
iOS / Android
Mobile App
React
Native
Elastic
APM
Agent
React Web App
Azure
Static Web Apps
React Web App
Elastic Cloud に CORS を設定
# Note that the syntax for user settings can change between major versions.
# You might need to update these user settings before performing a major version upgrade.
#
# Slack integration for versions 7.0 and later must use the secure key store method.
# For more information, see:
# https://www.elastic.co/guide/en/elasticsearch/reference/current/actions-
slack.html#configuring-slack
(中略)
# from: Watcher
http.cors.enabled: true
http.cors.allow-credentials: true
http.cors.allow-origin: "*"
http.cors.allow-headers: X-Requested-With, X-Auth-Token, Content-Type, Content-Length,
Authorization, Access-Control-Allow-Headers, Accept
#
# HipChat and PagerDuty integration are also supported. To learn more, see the documentation.
• elasticsearch.yml
APM Real User Monitoring
JavaScript Agent Reference
• 概要
• https://www.elastic.co/guide/en/apm/agent/rum-js/5.x/index.html
• React Integration
• https://www.elastic.co/guide/en/apm/agent/rum-js/5.x/react-
integration.html#react-integration
npm install @elastic/apm-rum-react –save
import { ApmRoute } from '@elastic/apm-rum-react’
import { withTransaction } from '@elastic/apm-rum-react'
Elastic Cloud → Kibana で APM モニタリング
https://cloud.elastic.co/home
まとめ
まとめ
l API プロジェクトの作成
l Entity Framework Core コード作成と最初の Database ⽣成
l API エンドポイント⽣成
l React プロジェクトの作成
l Azure App Service にサーバを公開
l Azure Static Web App + GitHub Actions に React を公開
l CORS ポリシーの修正
l Elastic APM によるアプリケーションの監視
リソース
• チュートリアル: ASP.NET Core で Minimal Web API を作成する
https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/min-web-api?view=aspnetcore-6.0&tabs=visual-studio
• Entity Framework Core ツールのリファレンス - .NET Core CLI
https://docs.microsoft.com/ja-jp/ef/core/cli/dotnet
• Create React App:
https://reactjs.org/docs/create-a-new-react-app.html
• ASP.NET Core で React プロジェクト テンプレートを使⽤する
https://docs.microsoft.com/ja-jp/aspnet/core/client-side/spa/react?view=aspnetcore-6.0&tabs=visual-studio
• Install Bootstrap to React:
https://create-react-app.dev/docs/adding-bootstrap/
• GitHub Actions のワークフロー構⽂
https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions
Elastic リソース
• 公式ドキュメント
https://www.elastic.co/guide/index.html
• クラウドネイティブ アプリでの Elasticsearch
https://docs.microsoft.com/ja-jp/dotnet/architecture/cloud-
native/elastic-search-in-azure
• Azure での検索データ ストアの選択
https://docs.microsoft.com/ja-jp/azure/architecture/data-
guide/technology-choices/search-options
• Elastic APM Agent
https://www.elastic.co/guide/en/apm/agent/index.html
• APM
https://www.elastic.co/jp/apm/
• Configuration on .NET Core
https://www.elastic.co/guide/en/apm/agent/dotnet/current/co
nfiguration-on-asp-net-core.html
• ASP.NET Core Quick Start
https://www.elastic.co/guide/en/apm/agent/dotnet/current/set
up-asp-net-core.html
Elastic Community Conference on Feb. 11-12, 2022
https://sessionize.com/elastic-community-conference/
Japan Track が公開予定︕
(⽇本からスピーカー4社5名が
登壇予定)
Elastic Meetup
https://www.meetup.com/ja-JP/Tokyo-Elastic-Fantastics/events/283113192/
Developers Summit 2022
https://event.shoeisha.jp/devsumi/20220217/session/3724/
デジタルカスタマーエクスペリエンスの向上 - Enterprise Search と Observability
Web サイトやモバイルアプリをスムースに運営し、購⼊からカスタマーサポートまで顧客との繋がりを⼤切にするには、検索機能や可観測性の強化が、カスタマーエクスペリエンス向上のための
秘策です。Elastic はオープンでフリーな超⾼速検索エンジンとデータの出⼊⼒インターフェースから構成され、あらゆるパブリッククラウドにデプロイできる、業界をリードする検索・分析プラット
フォームです。Elastic により企業や個⼈は魅⼒的な顧客 Web サイト体験やモバイルアプリ体験を提供できます。このセッションでは、Elastic による実装をサンプルアプリのデモを交えなが
らご紹介していきます。
Thank you for your attention!

Let's build a simple app with .net 6 asp.net core web api, react, and elastic apm.

  • 1.
    .NET 6 ASP.NETCore Web API、 React、Elastic APM でシンプルなアプリを 構築してみよう 鈴⽊ 章太郎 Elastic テクニカルプロダクトマーケティングマネージャー/エバンジェリスト デジタル庁 省庁業務グループ ソリューションアーキテクト
  • 2.
  • 3.
    l API プロジェクトの作成 lEntity Framework Core コード作成と最初の Database ⽣成 l API エンドポイント⽣成 l React プロジェクトの作成 l Azure App Service にサーバを公開 l Azure Static Web App + GitHub Actions に React を公開 l CORS ポリシーの修正 l Elastic APM によるアプリケーションの監視 l まとめ アジェンダ
  • 4.
    今回は Visual Studio 2022for Mac Preview で デモアプリを作成 https://visualstudio.microsoft.com/ja/vs/mac/preview/ 加えて、 Azure Data Explorer、 GitHub Desktop for mac、 Visual Studio Code
  • 5.
    今回のデモアプリのイメージ Azure SQL Database Elastic Cloud 東⽇本リージョン マスターノードx 1 データノード x 2 ML ノード x 1 https://f79...c67.japaneast .azure.elastic- cloud.com:9243/ 全⽂検索クエリ CRUD 検索・更新 UI Azure サブスクリプション Visual Studio 2022 for Mac Azure App Service Elastic APM Endpoint に送信 Azure Data Explorer ASP.NET 6 Web API Elastic APM Agent iOS / Android Mobile App React Native Elastic APM Agent React Web App Azure Static Web Apps React Web App
  • 6.
  • 7.
    Lightweight, single-file, cloudnative APIs Low ceremony, top-level C# programs Easy to get started Path to MVC .NET 6 Minimal APIs for Cloud Native Apps var app = WebApplication.Create(args); app.MapGet("/", () => "Hello World!"); app.Run(); Minimal code for minimal apps • C#のトップレベルプログラムをベースに最⼩限のコードで ASP.NET Web API を構築する軽量な⽅法 • 従来のコントローラースタイルの Web API のようなコードなしにクラウドスケール API を簡単に構築可能 • プロジェクトが⼤きくなり始めたら、これらを ASP.NET MVC コントローラに移⾏することも可能 • パイプライン処理や単純な CRUD のような複雑さを必要としない API を迅速に構築するための選択肢
  • 8.
  • 9.
    ASP.NET Core WebAPI Minimal API を使うので、真ん中のチェックは外す)
  • 10.
    ASP.NET Core WebAPI プロジェクト名は、aspnetserver (ソリューション名は任意)にする
  • 11.
    ASP.NET Core WebAPI この状態でデバッグ実⾏
  • 12.
    ASP.NET Core WebAPI この状態でデバッグ実⾏
  • 13.
    不要なコードを削除 – Program.cs(Startup.cs は廃⽌) • WeatherForecast 関連のソースコードを全て除去し保存 var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.Run();
  • 14.
    Entity Framework Coreコード作成と 最初の Database ⽣成
  • 15.
    Data フォルダーの作成と SealedClass 作成 Post クラスを Sealed で作成し継承できないようにする
  • 16.
    Post Class • attributeとして System.ComponentModel.DataAnnotations を追加し、クラスを作成する using System.ComponentModel.DataAnnotations; namespace aspnetserver.Data { internal sealed class Post { [Key] public int PostId { get; set; } [Required] [MaxLength(100)] public string Title { get; set; } = string.Empty; [Required] [MaxLength(100000)] public string Content { get; set; } = string.Empty; } }
  • 17.
    NuGet Package 追加 •NuGet Package Manager 起動 • EntityFrameworkCore • EntityFrameworkCore.Design • EntityFrameworkCore.SQLServer を追加 • Entity Framework Core CLI ツールインストール dotnet tool install --global dotnet-ef
  • 18.
    AppDbContext 作成 • AzureSQL Database 上に AppDB を⽣成しつつ Seed を作成 using Microsoft.EntityFrameworkCore; namespace aspnetserver.Data { internal sealed class AppDBContext : DbContext { public DbSet<Post> Posts {get; set;} protected override void OnConfiguring(DbContextOptionsBuilder dbContextOptionsBuilder) => dbContextOptionsBuilder.UseSqlServer("Server=tcp:spaappdev.database.windows.net,1433; Initial Catalog=AppDb;Persist Security Info=False;UserID=shotaro;Password=(password)#; MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"); protected override void OnModelCreating(ModelBuilder modelBuilder) { Post[] postsToSeed = new Post[6]; for (int i = 1; i <= 6; i++) { postsToSeed[i - 1] = new Post { PostId = i, Title = $"Post {i}", Content = $"これは投稿{i}で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している." }; } modelBuilder.Entity<Post>().HasData(postsToSeed); } } }
  • 19.
    dotnet ef migrationsadd dotnet ef migrations add 1stMigration -o "Data/Migrations"
  • 20.
    Entity Framework によるDB 作成とサンプルデータ確認 using Microsoft.EntityFrameworkCore.Migrations; #nullable disable namespace aspnetserver.Data.Migrations { public partial class _1stMigration : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Posts", columns: table => new { PostId = table.Column<int>(type: "int", nullable: false).Annotation("SqlServer:Identity", "1, 1"), Title = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false), Content = table.Column<string>(type: "nvarchar(max)", maxLength: 100000, nullable: false) }, constraints: table => { table.PrimaryKey("PK_Posts", x => x.PostId); }); migrationBuilder.InsertData( table: "Posts", columns: new[] { "PostId", "Content", "Title" }, values: new object[,] { { 1, "これは投稿 1 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 1" }, { 2, "これは投稿 2 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 2" }, { 3, "これは投稿 3 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 3" }, { 4, "これは投稿 4 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 4" }, { 5, "これは投稿 5 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 5" }, { 6, "これは投稿 6 で、⾮常に興味深い内容です。私はまた、YouTube ビデオを鑑賞するのが好きで、購読している.", "Post 6" } }); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "Posts"); } } }
  • 21.
    dotnet ef databaseupdate dotnet ef database update Azure Data Studio
  • 22.
    DB操作のためのレポジトリ (PostsRepositry.cs) 作成 usingMicrosoft.EntityFrameworkCore; namespace aspnetserver.Data { internal static class PostsRepository { internal async static Task<List<Post>> GetPostsAsync() { using (var db = new AppDBContext()) { return await db.Posts.ToListAsync(); } } internal async static Task<Post> GetPostByIdAsync(int postId) { using (var db = new AppDBContext()) { return await db.Posts .FirstOrDefaultAsync(post => post.PostId == postId); } } internal async static Task<bool> CreatePostAsync(Post postToCreate) { using (var db = new AppDBContext()) { try { await db.Posts.AddAsync(postToCreate); return await db.SaveChangesAsync() >= 1; } catch (Exception e) { return false; } } } internal async static Task<bool> UpdatePostAsync(Post postToUpdate) { using (var db = new AppDBContext()) { try { db.Posts.Update(postToUpdate); return await db.SaveChangesAsync() >= 1; } catch (Exception e) { return false; } } } internal async static Task<bool> DeletePostAsync(int postId) { using (var db = new AppDBContext()) { try { Post postToDelete = await GetPostByIdAsync(postId); db.Remove(postToDelete); return await db.SaveChangesAsync() >= 1; } catch (Exception e) { return false; } } } } }
  • 23.
  • 24.
    Program.cs 修正 –MapGet 追加(全て) • using aspnetserver.Data を追加し、クラスを作成する using aspnetserver.Data; --- app.MapGet("/get-all-posts", async () => await PostsRepository.GetPostsAsync()) .WithTags("Posts Endpoints"); app.Run();
  • 25.
    Program.cs 修正 –Swagger 修正 • SwaggerDoc を作成する • Swagger UI を開発環境以外でも 表⽰するように書き換える • launchSettings.json の "launchUrl": "swagger" ⾏をコメントアウトする 理由︓HTTP Local Host のみ(起動 URL なし)でアプリが起動し、次にポート番号なしで Swagger が起動するため using aspnetserver.Data; --- builder.Services.AddSwaggerGen(swaggerGenOptions => { swaggerGenOptions.SwaggerDoc("v1", new OpenApiInfo { Title = "ASP.NET Core 6 Web API React Sample", Version = "v1" }); }); app.UseSwagger(); app.UseSwaggerUI(swaggerUIOptions => { swaggerUIOptions.DocumentTitle = "ASP.NET Core 6 Web API React Sample"; swaggerUIOptions.SwaggerEndpoint("/swagger/v1/swagger.json", "超シンプルな Post モデルを提供する Web API."); swaggerUIOptions.RoutePrefix = string.Empty; });
  • 26.
    Program.cs 修正 –MapGet(postId) 追加 • MapGet(postId)、MapPost、MapPut、MapDelete app.MapGet("/get-post-by-id/{postId}", async (int postId) => { Post postToReturn = await PostsRepository.GetPostByIdAsync(postId); if (postToReturn != null) { return Results.Ok(postToReturn); } else { return Results.BadRequest(); } }).WithTags("Posts Endpoints");
  • 27.
    Program.cs 修正 –MapPost 追加 • MapGet(postId)、MapPost、MapPut、MapDelete app.MapPost("/create-post", async (Post postToCreate) => { bool createSuccessful = await PostsRepository.CreatePostAsync(postToCreate); if (createSuccessful) { return Results.Ok("作成しました︕"); } else { return Results.BadRequest(); } }).WithTags("Posts Endpoints");
  • 28.
    Program.cs 修正 –MapPut 追加 • MapGet(postId)、MapPost、MapPut、MapDelete app.MapPut("/update-post", async (Post postToUpdate) => { bool updateSuccessful = await PostsRepository.UpdatePostAsync(postToUpdate); if (updateSuccessful) { return Results.Ok("更新しました︕"); } else { return Results.BadRequest(); } }).WithTags("Posts Endpoints");
  • 29.
    Program.cs 修正 –MapDelete 追加 • MapGet(postId)、MapPost、MapPut、MapDelete app.MapDelete("/delete-post-by-id/{postId}", async (int postId) => { bool deleteSuccessful = await PostsRepository.DeletePostAsync(postId); if (deleteSuccessful) { return Results.Ok("削除しました︕"); } else { return Results.BadRequest(); } }).WithTags("Posts Endpoints");
  • 30.
  • 31.
  • 32.
    React シングルページアプリケーションの作成 • Terminalでルートフォルダに移動 • コマンドでフォルダと React プロジェクトを⽣成する • React ディレクトリに移動し git 関連を削除(全体で作成するため)し、VSCode を起動 npx create-react-app reactclient cd reactclient npm start sudo rm -r.git code ./
  • 33.
    Visual Studio Codeの起動と設定 • Terminal を開き下記のコマンドを打つ • 実⾏ (port 3000) npm install --save -cross-env npm start
  • 34.
    Visual Studio Codeの拡張機能追加とファイルの整理 • 拡張機能を追加(任意) • Bracket Pair Colorizer 2 v0.2.2 • Prettier • VS Code ES7 React/Redux/React-Native/JS snippets • Material Icon Theme • 不要なファイルの削除 • setupTests.js • reportWebVitals.js • logo.svg • index.css • App.Test.js • App.css • Index.js の編集 • 制限モード削除 ReactDOM.render(<App />,document.getElementById('root')); • reportWebVitals();、import reportWebVitals from ʻ./reportWebVitalsʻ;、import './index.css'; 削除
  • 35.
    CORS ポリシーの追加 • Server– Program.cs builder.Services.AddCors(options => { options.AddPolicy("CORSPolicy", builder => { builder .AllowAnyMethod() .AllowAnyHeader() .WithOrigins("http://localhost:3000", "https://appname.azurestaticapps.net/"); }); });
  • 36.
    React アプリで実装する内容と実⾏結果 • Bootstrapの追加 • 投稿をテーブルで表⽰ • サーバープロジェクト側の CORS ポリシー追加 • テーブルにデータを表⽰ • API エンドポイント付き定数 ファイル • データ新規追加フォーム コンポーネントの作成 • データ更新フォーム コンポーネントの作成 • データの削除
  • 37.
    Azure App Serviceにサーバを公開
  • 38.
    Visual Studio 2022for mac で Azure に公開 • Wizard を使⽤してホスティングプランは F1- Free に変更しながら deploy https://aspnet6webapireactsample.azurewebsites.net/index.html
  • 39.
    Azure Static WebApp + GitHub Actions で React を公開
  • 40.
    • 静的サイトに最適化されたホスティング環境 • CI/CDを GitHub Actions と統合 • Azure Functions によるサーバーレス API の統合 Microsoft Azure www Azure Static Web Apps
  • 41.
    • GitHub 統合 •統合 API サポート • Web アプリケーションの構築 • 静的サイトの発⾏ • Web アプリケーションのデプロイ • 認証プロバイダーの統合 • Azure Active Directory、Facebook、 Google、GitHub、 Twitter Azure Static Web Apps の機能 Web API + リッチな Web UI を実現する機能を提供
  • 42.
    https:/ / .. . Blazor + https:/ / . . . + JS JavaScript / C# による静的 Web サイトの開発・ホスト
  • 43.
    Azure Static WebApp と GitHub Actions ワークフロー • Azure Static Web Apps では、必要なすべての Azure リソースのプロビジョニングが ⾃動的に実施 • アプリをホストする前、変更を⾏う際、アプリをビルドするためのリポジトリへのコミットま たは pull request によって⾏う必要あり • Azure Static Web Apps の主な機能としてアプリケーションをビルドして発⾏するため の GitHub Actions ワークフローを設定できる • Azure Static Web Apps リソースを作成すると、GitHub Actions ワークフローが 作成される • ワークフローはすぐにトリガーされ、アプリのビルドと発⾏が⾏われる • リポジトリ内の監視対象ブランチに変更を加えるたびに、ワークフローがトリガーされる
  • 44.
    Azure Static WebApps 2つの⾃動化された側⾯ 1.アプリを構成する基になる Azure リソースのプロビジョニング 2.アプリケーションをビルドして発⾏する GitHub Actions ワークフロー • Azure Static Web Apps で Web にアプリを発⾏すると、 Web アプリとスケーラブルな API のホストが⾼速になる • GitHub Actions によって提供される統合ビルドおよびデプロイ ワークフローが得られる
  • 45.
    Static Web Appsインスタンスを GitHub に接続 • Static Web Apps インスタンスを作成する には、GitHub にサインインし、アプリのコード が含まれているリポジトリを指定 • アプリを⾃動的にビルドしてデプロイできるよう に、リポジトリ内に3つのフォルダーパスを指定 • アプリのビルド出⼒はアプリケーションのビルド 出⼒ディレクトリへの相対パス • 例︓ビルドされた資産を my-app/dist フォル ダーに出⼒するアプリが my-app にある場合、 この場所には dist を指定 • ビルドの詳細画⾯でビルドのプリセットを選択 • React を選択すると⾃動的に • アプリの場所 • アプリのビルド出⼒の場所 • API の場所 の3つに値が⼊⼒される 今回は / 直下ではなくて /reactclient にしないと動かない ので注意︕
  • 46.
    GitHub Actions を使⽤してソースコードから静的資産へ •GitHub リポジトリにはソース コードが格納され、発⾏する 前にビルドする必要あり • Static Web Apps インスタンスを作成すると、⾃動的にリ ポジトリに GitHub Actions ワークフローが作成される • このワークフローによって、追跡するように選択したブランチに 対して変更内容をプッシュしたり、pull request を作成し たりするたびに、アプリがビルドされる。これにより、ソースコー ドが静的資産に変換され、Azure によって提供される • ビルドが完了すると、アクションによって資産がデプロイされる • その GitHub アクションはリポジトリ の .github/workflows フォルダーに追加される • 必要に応じて、このファイルを確認または変更できる • リソースの作成時に⼊⼒した設定は、GitHub アクションの ファイルに保存される
  • 47.
    Azure Functions と統合されたAPI • API が必要な場合 • Azure Functions プロジェクトとしてリポジトリ 内に実装できる • API は⾃動的にデプロイされ、Static Web Apps インスタンスによってホストされる • GitHub Actions ワークフローによって、指定 したフォルダーの名前でリポジトリ内の API が 検索される • 通常、API アプリは api または functions と いう名前のフォルダーに配置されるところ、好きな 名前を付けることができる • 指定したフォルダー内に API が検出され ない場合 • API は発⾏されず、アプリのみ発⾏される 例︓ https://docs.microsoft.com/ja-jp/learn/modules/publish-static-web-app-api-preview-url/3-build-api
  • 48.
    GitHub Actions を使⽤してソースコードから静的資産へ •GitHub リポジトリの [Actions](アクション) ページに移動し、ビルドおよびデプロイアクションの 状態を確認できる • Visual Studio Code エディターで 例︓my-first-static-web-app を右クリックし[ポータルで開く] • [Azure Static Web Apps CI/CD] を選択 • ci: add Azure Static Web Apps workflow file のようなタイトルのついた最上位のコミットを選択 • 左にある [Build and Deploy Job]リンクを選択。ここから、ビルド時にアプリの進⾏状況を確認できる
  • 49.
    Web サイトを表⽰する • GitHubアクションによって Web アプリのビルドと発⾏が完了すると実⾏中のアプリを参照して 確認できる • Azure portal の [URL] リンクを選択して、ブラウザーでアプリにアクセス
  • 50.
  • 51.
    CORS ポリシーの修正 • Server– Program.cs • Client – Constants.js builder.Services.AddCors(options => { options.AddPolicy("CORSPolicy", builder => { builder .AllowAnyMethod() .AllowAnyHeader() .WithOrigins("http://localhost:3000", "https://pine-tree-04bf7ae98.2.azurestaticapps.net/"); }); }); Azure Static Web Apps に デプロイ後に決まるので最初は ~appname.azurestaticapps. net ~ 等にしておく︕ const API_BASE_URL_DEVELOPMENT = 'https://localhost:7293'; const API_BASE_URL_PRODUCTION = 'https://net6webapireactsample.azurewebsites.net/';
  • 52.
  • 53.
    今回のデモアプリのイメージ Azure SQL Database Elastic Cloud 東⽇本リージョン マスターノードx 1 データノード x 2 ML ノード x 1 https://f79...c67.japaneast .azure.elastic- cloud.com:9243/ 全⽂検索クエリ CRUD 検索・更新 UI Azure サブスクリプション Visual Studio 2022 for Mac Azure App Service Elastic APM Endpoint に送信 Azure Data Explorer ASP.NET 6 Web API Elastic APM Agent iOS / Android Mobile App React Native Elastic APM Agent React Web App Azure Static Web Apps React Web App
  • 54.
    Elastic Cloud にCORS を設定 # Note that the syntax for user settings can change between major versions. # You might need to update these user settings before performing a major version upgrade. # # Slack integration for versions 7.0 and later must use the secure key store method. # For more information, see: # https://www.elastic.co/guide/en/elasticsearch/reference/current/actions- slack.html#configuring-slack (中略) # from: Watcher http.cors.enabled: true http.cors.allow-credentials: true http.cors.allow-origin: "*" http.cors.allow-headers: X-Requested-With, X-Auth-Token, Content-Type, Content-Length, Authorization, Access-Control-Allow-Headers, Accept # # HipChat and PagerDuty integration are also supported. To learn more, see the documentation. • elasticsearch.yml
  • 55.
    APM Real UserMonitoring JavaScript Agent Reference • 概要 • https://www.elastic.co/guide/en/apm/agent/rum-js/5.x/index.html • React Integration • https://www.elastic.co/guide/en/apm/agent/rum-js/5.x/react- integration.html#react-integration npm install @elastic/apm-rum-react –save import { ApmRoute } from '@elastic/apm-rum-react’ import { withTransaction } from '@elastic/apm-rum-react'
  • 56.
    Elastic Cloud →Kibana で APM モニタリング https://cloud.elastic.co/home
  • 57.
  • 58.
    まとめ l API プロジェクトの作成 lEntity Framework Core コード作成と最初の Database ⽣成 l API エンドポイント⽣成 l React プロジェクトの作成 l Azure App Service にサーバを公開 l Azure Static Web App + GitHub Actions に React を公開 l CORS ポリシーの修正 l Elastic APM によるアプリケーションの監視
  • 59.
    リソース • チュートリアル: ASP.NETCore で Minimal Web API を作成する https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/min-web-api?view=aspnetcore-6.0&tabs=visual-studio • Entity Framework Core ツールのリファレンス - .NET Core CLI https://docs.microsoft.com/ja-jp/ef/core/cli/dotnet • Create React App: https://reactjs.org/docs/create-a-new-react-app.html • ASP.NET Core で React プロジェクト テンプレートを使⽤する https://docs.microsoft.com/ja-jp/aspnet/core/client-side/spa/react?view=aspnetcore-6.0&tabs=visual-studio • Install Bootstrap to React: https://create-react-app.dev/docs/adding-bootstrap/ • GitHub Actions のワークフロー構⽂ https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions
  • 60.
    Elastic リソース • 公式ドキュメント https://www.elastic.co/guide/index.html •クラウドネイティブ アプリでの Elasticsearch https://docs.microsoft.com/ja-jp/dotnet/architecture/cloud- native/elastic-search-in-azure • Azure での検索データ ストアの選択 https://docs.microsoft.com/ja-jp/azure/architecture/data- guide/technology-choices/search-options • Elastic APM Agent https://www.elastic.co/guide/en/apm/agent/index.html • APM https://www.elastic.co/jp/apm/ • Configuration on .NET Core https://www.elastic.co/guide/en/apm/agent/dotnet/current/co nfiguration-on-asp-net-core.html • ASP.NET Core Quick Start https://www.elastic.co/guide/en/apm/agent/dotnet/current/set up-asp-net-core.html
  • 61.
    Elastic Community Conferenceon Feb. 11-12, 2022 https://sessionize.com/elastic-community-conference/ Japan Track が公開予定︕ (⽇本からスピーカー4社5名が 登壇予定)
  • 62.
  • 63.
    Developers Summit 2022 https://event.shoeisha.jp/devsumi/20220217/session/3724/ デジタルカスタマーエクスペリエンスの向上- Enterprise Search と Observability Web サイトやモバイルアプリをスムースに運営し、購⼊からカスタマーサポートまで顧客との繋がりを⼤切にするには、検索機能や可観測性の強化が、カスタマーエクスペリエンス向上のための 秘策です。Elastic はオープンでフリーな超⾼速検索エンジンとデータの出⼊⼒インターフェースから構成され、あらゆるパブリッククラウドにデプロイできる、業界をリードする検索・分析プラット フォームです。Elastic により企業や個⼈は魅⼒的な顧客 Web サイト体験やモバイルアプリ体験を提供できます。このセッションでは、Elastic による実装をサンプルアプリのデモを交えなが らご紹介していきます。
  • 64.
    Thank you foryour attention!