AWS Lambda用に.NET Coreで書いたAPIサーバをDockerでローカルでも実行させてみた
はじめに
.NET CoreもC#も未経験の筆者が、AWS上で.NET Core Webアプリケーションを作成する必要に駆られたので、お試しでデプロイとローカルデバグ用の開発環境を構築してみました。
備忘録を兼ねてメモを残します (テック記事は人のためならず)。
これはアカンとか、何書いているのププみたいなことがあればご指摘いただけますと、たいへんありがたいです。
Introduction
今回の要件は以下の通りです。
- 開発は.NET Core (この記事執筆時点では2.1)
- 開発言語はC#
- 動作環境はAWS Serverless Architecture (APIGateway + Lambda + DynamoDB etc.)
なお、開発環境としてはVisual Studio 2019を使用しています。
なお、機能として、"ASP.NETとWeb開発"と".NET Coreクロスプラットフォームの開発"をインストールしています。
準備
プロジェクトを開始する前に、以下の準備を行います。
AWS toolkit for Visual Studio 2017 and 2019
拡張機能としてVisual Studio Marketplaceから、"AWS toolkit for Visual Studio 2017 and 2019"をインストールします。
"拡張機能"メニュー → "拡張機能の管理" → "AWS toolkit for Visual Studio 2017 and 2019"を検索してインストール
AWS Explorer
Visual StudioでAWS Explorerを起動し、今回の環境を実行するAWSへのアクセス情報Profileを作成します。
"表示"メニュー → "AWS Explorer" → "New Account Profile"ボタン
Profileの各項目は以下のように入力します。
- Profile Name
- default
- Storage Location
- Shared Credentials File
- Access Key ID
- 今回の作業に必要な権限を持つユーザーのAccess Key
- Secret Access Key
- 同じくユーザーのSecret Access Key
- Account Number
- AWSアカウントID
aws configure
Power Shellなどを起動し、aws configureコマンドを実行して、AWSへの接続情報を入力します。
PS C:\Users\hogehoge> aws configure
- AWS Access Key ID
- 上記のprofileと同じAccess Key
- AWS Secret Access Key
- 上記のprofileと同じSecret Access Key
- Default region name
- ap-northeast-1
- Default output format
- json
Docker Desktop
dockerを使用しますので、Docker Desktopをインストールします。
Docker Desktopを実行する場合は、コントロールパネルで、Windowsの機能として"Hyper-V"と"Windows ハイパーバイザープラットフォーム"を有効にする必要があります。なお、"Hyper-V"を使用するため"VirtualBox"アプリケーションは使用できなくなってしまいます(残念)。
REST API用ツール
テスト用に、REST APIにアクセスするためのツールやブラウザのextensionがあると便利です。
ソリューション作成
Visual Studioを使って、冒頭の要件に合うプロジェクトを作っていきます。
今回は、プロジェクトテンプレート"AWS Serverless Application with Tests (.NET Core - C#)"内の"Blog API using DynamoDB"をテンプレートとして使用します。
また、ローカル環境上でDockerコンテナとして動作するASP.NET Coreアプリケーションも同時に作成します。これにより、AWS環境がなくてもデバグが可能になります(今回はSAM(Serverless Application Model)のlocal環境機能は使用しません)。
ソリューション(Serverless Applicationプロジェクト)新規作成
Visual Studioを起動し、新しいプロジェクトの作成ダイアログを起動します。
"ファイル"メニュー → "新規作成" → "プロジェクト"
テンプレートとして、"AWS Serverless Application with Tests (.NET Core - C#)"を選択します。
なお、今回は、プロジェクト名とソリューション名を"Hoge"にし、Blueprintとして"Blog API using DynamoDB"を選択します。
すると、ソリューションには、Serverless Application用プロジェクト"Hoge"と、そのテスト用プロジェクト"Hoge.Tests"が作成されているはずです。
共通プロジェクト
.NET Coreを使用したServerless Applicationと、ローカルのDockerで動作させるためのASP.NET Coreアプリケーションの両方で共通で使用するたの.NET Core Library用のプロジェクトを作成します。
再び、新規プロジェクトを作成しますが、今度は、プロジェクトテンプレートとして"クラス ライブラリ(.NET Core)"を選択します。
ソリューションエクスプローラで、ソリューション"Hoge"を右クリック → "追加" → "新しいプロジェクト"
プロジェクト名を"HogeLib"にして、プロジェクトを作成します。
プロジェクトを作成したら、対象のフレームワークを".NET Core 2.1"に変更します。
ソリューションエクスプローラで、"HogeLib"プロジェクトを右クリック → "プロパティ" → "対象のフレームワーク"を変更
参照関係の修正
"Hoge"プロジェクトから"HogeLib"を参照できるように、設定を追加します。
ソリューションエクスプローラで、"Hoge"を右クリック → "追加" → "参照"
参照するプロジェクトとして"HogeLib"のチェックボックスを有効にしてから、"OK"を押します。
共通コードの作成
今回の共通コードの作成は、HogeプロジェクトのBlog.csモデルクラスを、HogeLibプロジェクトに移動させるだけにします。
本来であれば、ロジックも移動させるのですが、説明と修正を簡単にするために、今回はモデルクラスのみの移動とします。
モデルクラスの移動は以下のように行いました (JetBrains社のRiderがあれば簡単なんですが・・・)。
- HogeLib/Class1.csはもう必要無いので削除
- HogeLibプロジェクト下に新しいフォルダー"Models"を作成
- Hoge/Blog.csをHogeLib/Modelsフォルダの下にMove
- HogeLib/Models/Blog.csのnamespaceを"Hoge"から"HogeLib.Models"に変更
- Hoge/Functions.csに"using HogeLib.Models"を追加
- Hoge.Tests/FunctionTest.csに"using HogeLib.Models"を追加
Regionの変更
"Blog API using DynamoDB"BluePrintで生成されたコードはそのままではRegionの関係で動かないので、コードを修正します。
//this.DDBContext = new DynamoDBContext(new AmazonDynamoDBClient(), config);
this.DDBContext = new DynamoDBContext(new AmazonDynamoDBClient(RegionEndpoint.APNortheast1), config);
//this.DDBClient = new AmazonDynamoDBClient(RegionEndpoint.USWest2);
this.DDBClient = new AmazonDynamoDBClient(RegionEndpoint.APNortheast1);
Publish
これで、Serverless Application用の修正は完了しましたので、ひとまずAWSにPublishします。
ソリューションエクスプローラで、"Hoge"を右クリック → "Publish to AWS Lambda..."
ダイアログの各項目は、以下のように設定します。
なお、Publishに使用するS3バケットは、予めAWSコンソールで作成しておくか、ダイアログ上の"New..."ボタンで作成します。
- Account profile to use
- default
- Region
- Asia Pacific (Tokyo)
- Build Settings Configuration
- Release
- Framework
- netcoreapp2.1
- Stack Name
- HogeStackFoo (Fooは他の人と被らないようにするための適当な文字列。同一AWSアカウント内で衝突しないように)
- S3 Bucket
- hogestackfoo (Fooは他の人と被らないようにするための適当な文字列。なお上記のstackと同じである必要は無い。S3の命名規則に注意)
- BlogTableName
- Hoge-Blog-Foo (Fooは他の人と被らないようにするための適当な文字列。同一AWSアカウント内で衝突しないように)
- ReadCapacity
- 1 (テスト用なので小さくてok)
- ShouldCreateTable
- true
- WriteCapacity
- 1 (テスト用なので小さくてok)
しばらくして、進行状況画面のStatusが"CREATE_IN_PROGRESS"から"CREATE_COMPLETE"になればPublish完了です。
なお、BlogTableNameを空にしておくと自動的に適当な名前をつけてくれるのですが、この後、Mock Lambda Test Toolでテーブル名を指定する必要があるので、ここでは明示的に名前を付けています。
Mock Lambda Test Tool
AWS上のlambdaを直接デバグするためのツール"Mock Lambda Test Tool"用の設定を変更します。
変更箇所は、環境変数BlogTableに、一つ前のフェーズでBlogTableNameに指定した名前を設定したことです。
{
"profiles": {
"Mock Lambda Test Tool": {
"commandName": "Executable",
"commandLineArgs": "--port 5050",
"workingDirectory": ".\\bin\\Debug\\netcoreapp2.1",
"executablePath": "C:\\Users\\%USERNAME%\\.dotnet\\tools\\dotnet-lambda-test-tool-2.1.exe",
"environmentVariables": {
"BlogTable": "Hoge-Blog-Foo"
}
}
}
}
ASP.NET Core用プロジェクトの追加
ローカルのdocker環境で動作させるためのASP.NET Coreアプリケーション用のプロジェクトを追加します。
プロジェクトテンプレートとして"ASP.NET Core Web アプリケーション (C#)"を選択します。
ソリューションエクスプローラで、ソリューション"Hoge"を右クリック → "追加" → "新しいプロジェクト"
プロジェクト名を"HogeWebApi"にします。その他の項目は以下のように設定します。
- SDK
- .NET Core ASP.NET Core 2.1
- プロジェクトテンプレート
- API
- 認証
- 認証なし
- HTTPS用の構成
- 無効(チェックを外す)
- Dockerサポートを有効にする
- 有効(チェックする)
- OS
- Linux
HogeWebApiからHogeLibを参照するようにします。
ソリューションエクスプローラで、"HogeWebApi"を右クリック → "追加" → "参照"
参照するプロジェクトとして"HogeLib"のチェックボックスを有効にしてから、"OK"を押します。
次に、NuGetパッケージとしてAWSSDK.DynamoDBv2"をインストールします。
ソリューションエクスプローラで、"HogeWebApi"を右クリック → "NuGetパッケージの管理"
ASP.NET Core用のクロスプラットフォーム用WebサーバであるKestrel用にユーザーシークレットを作成する必要があるので、以下の操作でユーザーシックレット用のファイルを作成しておきます(中身は空のままでかまいません)。
ソリューションエクスプローラで、"HogeWebApi"を右クリック → "ユーザーシークレットの管理"
また、今回"wwwroot"ディレクトリは使わないので、削除してしまいます。
docker-composeプロジェクトの追加
HogeWebApi ASP.NET Coreアプリケーションを動作させるためのオーケストレーション用プロジェクトとしてdocker-composeプロジェクトを手動で作成します。
エクスプローラ、PowerShellなどでHogeソリューションプロジェクトの直下に、"DockerCompose"というディレクトリを作成します。
そして、DockerCompose下に以下のVisual Studioのプロジェクトファイル(docker-compose.dcproj)をエディタ等で作成します。
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" Sdk="Microsoft.Docker.Sdk">
<PropertyGroup Label="Globals">
<ProjectVersion>2.1</ProjectVersion>
<DockerTargetOS>Linux</DockerTargetOS>
<ProjectGuid>7530e2b9-bdab-44ab-87df-c6e6fc69b1b2</ProjectGuid>
<DockerLaunchAction>None</DockerLaunchAction>
<DockerServiceName>hogewebapi</DockerServiceName>
</PropertyGroup>
</Project>
このプロジェクトファイルをソリューション"Hoge"に追加します。
ソリューションエクスプローラで、ソリューション"Hoge"を右クリック → "追加" → "既存のプロジェクト"
docker-composeプロジェクトの作成
まず、localで動作するdynamodbのDockerコンテナを作成します。
docker-composeプロジェクトに"localdynamodb"フォルダを作成します。
ソリューションエクスプローラで、"docker-compose"を右クリック → "追加" → "新しいフォルダー" → "localdynamodb"にリネーム
localdynamodbフォルダの下に以下の二つのファイルをエディタ等で作成します。
FROM openjdk:8-jre
RUN mkdir /var/dynamodb_local
WORKDIR /var/dynamodb_local
RUN curl -fsSL https://s3-ap-northeast-1.amazonaws.com/dynamodb-local-tokyo/dynamodb_local_latest.tar.gz -o dynamodb.tar.gz \
&& tar xzf dynamodb.tar.gz \
&& rm dynamodb.tar.gz
COPY entrypoint.sh /bin/
RUN chmod 755 /bin/entrypoint.sh
ENTRYPOINT ["/bin/entrypoint.sh"]
EXPOSE 8000
# !/bin/sh
set -- java "$@"
set -- "$@" -Djava.library.path=/var/dynamodb_local/DynamoDBLocal_lib
set -- "$@" -jar /var/dynamodb_local/DynamoDBLocal.jar
set -- "$@" -sharedDb
set -- "$@" -dbPath /data
set -- "$@" -port 8000
exec "$@"
localdynamodbフォルダにこの二つのファイルを追加します。
ソリューションエクスプローラで、フォルダ"localdynamodb"を右クリック → "追加" → "既存の項目" → 今回作成した"Dockerfile"と"entrypoint.sh"を指定
docker-compose用設定ファイルを作成します。
エディタ等でDockerComposeフォルダ下に以下のファイルを作成します。
version: '3.4'
services:
hogewebapi:
image: ${DOCKER_REGISTRY-}hogewebapi
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=https://+:443;http://+:80
build:
context: ..
dockerfile: HogeWebApi/Dockerfile
container_name: hogewebapi
ports:
- "80"
- "443"
volumes:
- ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
- ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro
networks:
- private_net
localdynamodb:
build:
context: ./localdynamodb
container_name: localdynamodb
ports:
- "8000"
volumes:
- dynamodb-data:/data
networks:
- private_net
networks:
private_net:
volumes:
dynamodb-data:
docker-compose.ymlファイルをdocker-composeプロジェクトに追加します。
ソリューションエクスプローラで、"docker-compose"を右クリック → "追加" → "既存の項目" → 今回作成した"docker-compose.yml"を指定
ASP.NET Coreアプリケーション用コントローラの作成
ASP.NET Coreアプリケーション用のコントローラを作成します。
コントローラの内容はAWS Serverlessアプリケーション用のBlog処理と同じ内容になります。
まず、テンプレートで作成されたサンプル用コントローラを削除します。
HogeWebApi\Controllers\ValuesController.csを削除
次にBlog用コントローラを追加します。
コントローラーの種類は"APIコントローラー - 空"を選択します。コントローラー名は"BlogController"にします。
ソリューションエクスプローラで、"HogeWebApi"のフォルダ"Controllers"を右クリック → "追加" → "コントローラー" → "APIコントローラー -空"
BlogController.csの内容は以下のようになります。
なお、処理内容は、lambda用の.NET CORE アプリケーションのものとほぼ同様です (Hoge\Functions.cs)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using HogeLib.Models;
using Amazon;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.Model;
namespace HogeWebApi.Controllers {
[Route("blog")]
[ApiController]
public class BlogController : ControllerBase {
IDynamoDBContext DDBContext {
get; set;
}
/// <summary>
/// Default constructor that Controller will invoke.
/// </summary>
public BlogController() {
// Check to see if a table name was passed in through environment variables and if so
// add the table mapping.
var tableName = "Hoge_Blog";
AWSConfigsDynamoDB.Context.TypeMappings[typeof(Blog)] = new Amazon.Util.TypeMapping(typeof(Blog), tableName);
AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig { ServiceURL = "http://localdynamodb:8000" };
AmazonDynamoDBClient client = new AmazonDynamoDBClient("dummy", "dummy", clientConfig);
try {
// DynamoDB内にテーブルが無ければ新規作成する。もちろん本番用Controllerでこんなことをしてはいけない
Task<CreateTableResponse> response = client.CreateTableAsync(
tableName,
new List<KeySchemaElement> {
new KeySchemaElement("Id", KeyType.HASH)
},
new List<AttributeDefinition> {
new AttributeDefinition("Id", ScalarAttributeType.S)
},
new ProvisionedThroughput {
ReadCapacityUnits = 1,
WriteCapacityUnits = 1
},
new System.Threading.CancellationToken()
);
response.Wait();
} catch (Exception _) {
// すでにあるならなにもしない
}
var config = new DynamoDBContextConfig { Conversion = DynamoDBEntryConversion.V2 };
this.DDBContext = new DynamoDBContext(client, config);
}
// GET: blog
[HttpGet]
public async Task<IEnumerable<Blog>> GetAsync() {
var search = this.DDBContext.ScanAsync<Blog>(null);
var blogs = await search.GetNextSetAsync();
return blogs;
}
// GET: blog/blogId
[HttpGet("{blogId}", Name = "Get")]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Blog>> GetAsync(string blogId) {
if (string.IsNullOrEmpty(blogId)) {
return BadRequest();
}
var blog = await DDBContext.LoadAsync<Blog>(blogId);
if (blog == null) {
return NotFound();
}
return blog;
}
// PUT: blog
[HttpPut]
[ProducesResponseType(StatusCodes.Status202Accepted)]
public async Task<ActionResult> PutAsync([FromBody] Blog blog) {
blog.Id = Guid.NewGuid().ToString();
blog.CreatedTimestamp = DateTime.Now;
await DDBContext.SaveAsync<Blog>(blog);
return Accepted();
}
// DELETE: blog/blogId
[HttpDelete("{blogId}")]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status202Accepted)]
public async Task<ActionResult> DeleteAsync(string blogId) {
if (string.IsNullOrEmpty(blogId)) {
return BadRequest();
}
await this.DDBContext.DeleteAsync<Blog>(blogId);
return Accepted();
}
}
}
BlogController用にlaunchSettings.jsonを変更します。
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:60100",
"sslPort": 0
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "blog",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"HogeWebApi": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "blog",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:5000"
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/blog",
"publishAllPorts": true
}
}
}
実行してみる
これで準備ができました。Ready Perfectly。
これにより、以下のことが行えるようになります。
- AWSにデプロイして、API Gateway経由で.NET Core on Lambdaを呼び出せるようになる
- Testプロジェクトを実行することができる
- Mock Lambda Test Toolを使って、Lambdaのデバグが行える
- ローカルにDockerによるASP.NET Core環境を構築して、直接デバグすることができる
AWSデプロイ
開発中に一旦デプロイしていますが、もう一度デプロイ方法を記載しておきます。
ソリューションエクスプローラで、"Hoge"を右クリック → "Publish to AWS Lambda..."
デプロイすると、状況表示画面に"AWS Serverless URL"が表示されます。
見逃してしまった場合は、ブラウザでAWSコンソールのAPI Gatewayメニューで確認するか、AWS ExplorerのAWS CloudFormationのStackをダブルクリックします。
Serverless URLを確認したら、お好みのREST Clientツールで、以下のアドレスに"GET"メソッドでアクセスしてみます。
GET https://<<なんらかの文字列>>.execute-api.ap-northeast-1.amazonaws.com/Prod
初回起動時には、DynamoDBに何のデータも登録されていないので、空のJSON配列が返ってくるはずです。
[]
次にデータを登録してみます。同じURLにPUTしてみます。
また、本文には登録するデータを表すJsonを指定します。
PUT https://<<なんらかの文字列>>.execute-api.ap-northeast-1.amazonaws.com/Prod
Content-Type: text/json; charset=UTF-8
{"Name":"作者","Content":"中身"}
登録したデータのidがplain textで返ってくるはずです。
ここで、もう一度"GET"してみると、データが登録されているのが分かります。
[
{
"Id": "9612b9af-a4bc-45f6-a366-a33a7c03ec3d",
"Name": "作者",
"Content": "中身",
"CreatedTimestamp": "2020-01-17T01:31:56.002+00:00"
}
]
テスト
最初に、プロジェクトテンプレート"AWS Serverless Application with Tests (.NET Core - C#)"内の"Blog API using DynamoDB"をテンプレートとして作成していますので、自動的にテストケースが登録されているはずです。
それでは、テストを実行してみます。
ソリューションエクスプローラで、"Hoge.Tests"を右クリック → "テストの実行"
BlogTestAsyncが成功になっていれば、テストOKです。
すでにテスト済みになっていて、もう一度テストしたい場合には、ビルドメニューから"Hoge.Tests"をリビルドしてみてからテスト実行してください。
もちろん、テストをデバグモードで実行すると、テストコードや、そこから呼び出される本体コードをデバグすることができます。
Mock Lambda Test Tool
Visual Studio上でMock Lambda Test Toolを使用することで、直接Lambdaをテスト/デバグすることができます。
Mock Lambda Test Toolを起動すると、デフォルトブラウザに"localhost:5050"へのアクセスが行われて、AWS .NET Mock Lambda Test Tool (Preview)が表示されます。
試しに、Visual Studio上でFunctions.csを開き、関数GetBlogsAsync (GetBlogAsyncでは無い) にブレークポイントを張ります。
そして、AWS .NET Mock Lambda Test Toolに以下のように入力して、Execute Functionを実行します。
- Config File
- aws-lambda-tools-defaults.json
- Function
- GetBlogs
- AWS Credential Profile
- default
- AWS Region
- ap-northeast-1
- Example Requests
- API Gateway AWS Proxy
- リクエストの中身
- Get Blogsはリクエストを参照していないので、そのままでOKです。AddBlogなどをテストする場合は、適宜書き換えてください
実行すると、ブレークポイントで停止するはずです。
そのまま処理を続行すると、Responseとして、AWS デプロイの項目で登録した内容が表示されているはずです。
また、Log Outputには、GetBlogsAsync内のcontext.Loggerに出力したログ内容が表示されています。
Docker
Dockerを実行する場合は、docker-composeをスタートアッププロジェクトに設定します。
ソリューションエクスプローラで、"docker-compose"を右クリック → "スタートアッププロジェクトに設定"
docker-composeをビルド (なにかうまく行かないときはリビルドも試して・・)して、デバグを開始します。
なお、コンテナとして、hogewebapiとlocaldynamodbコンテナが起動しているはずです。
コンテナウィンドウでhogewebapiをクリックして、コンテナーポート80に対応するホストポートを調べます (327xxぐらい)。
または、PowerShellなどを使って、dockerコマンドでポートを調べてもかまいません。
PS C:\Users\hogehoge> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
932693b5dd9f dockercompose15818753263670802938_localdynamodb "/bin/entrypoint.sh" 20 minutes ago Up 20 minutes 0.0.0.0:32778->8000/tcp localdynamodb
375a9952a4b6 hogewebapi:dev "tail -f /dev/null" 20 minutes ago Up 16 minutes 0.0.0.0:32780->80/tcp, 0.0.0.0:32779->443/tcp hogewebapi
PS C:\Users\hogehoge>
お好みのREST Clientツールで、ローカルで起動しているASP.NET Coreアプリケーションにアクセスしてみます。
GET http://localhost:32780/blog
初回起動時には、DynamoDBに何のデータも登録されていないので、空のJSON配列が返ってくるはずです。
次にデータを登録してみます。同じURLにPUTしてみます。
また、本文には登録するデータを表すJsonを指定します。
PUT http://localhost:32780/blog
Content-Type: text/json; charset=UTF-8
{"Name":"作者2","Content":"中身2"}
登録に成功すると、202 Acceptedが返ってくるはずです。
ここで、もう一度"GET"してみると、データが登録されているのが分かります。
[
{
"id": "7cc5b03d-639d-40e8-8692-22b99e779c02",
"name": "作者2",
"content": "中身2",
"createdTimestamp": "2020-01-17T03:16:42.561+00:00"
}
]
もちろん、ブレークポイントを使ってデバグをすることもできます。
例えば、HogeWebApi/Controllerse/BlogController.csの各メソッドで試すことができます。
おまけ(GitHub)
作成したプロジェクトをGitHubに保存しておきます。
拡張機能としてVisual Studio Marketplaceから、"GitHub Extension for Visual Studio"をインストールします。
"拡張機能"メニュー → "拡張機能の管理" → "GitHub Extension for Visual Studio"を検索してインストール
次に表示メニューからチームエクスプローラーを選択し、GitHubに接続します。
"表示"メニュー → "チームエクスプローラー" → "GitHub" → GitHubアカウントでサインイン
必要であれば、Git設定を変更します(ユーザー名、メールアドレス、既定のリポジトリの場所 (今回作成したHogeソリューションの一つ上を指定))。
そして、ソリューション"Hoge"を右クリックして、"ソリューションをソース管理に追加"を選びます。
すると、「現在のソリューションには、ソリューションフォルダー外にあるなんとかかんとか統合してください。」とかいうダイアログが表示されますが、無視して"OK"ボタンを押してください。
チームエクスプローラーから"同期"を選んで、リポジトリ名などを入力すれば、GitHubへのPushが始まります。
こうしてできたリポジトリがこちらになります。
修正履歴
まだありません