20200731のMacに関する記事は4件です。

LabelImgをMacにインストール

labelImgとは

無料のアノテーション(画像ラベリング)ツールで、物体検出の教師データ作成に使用する。VoTTと比較して、UIが初心者向けなので、採用しました。

今回はインストール手順を記載。

labelImgのGithub
https://github.com/tzutalin/labelImg

インストール準備

ターミナルを開く。
image.png

brew更新(念のため)

brew update

gitの更新

brew install git

作業場所を作成

mkdir ~/Work
cd ~/Work

labelImgのダウンロード

git clone git@github.com:tzutalin/labelImg.git

gitの設定エラーがでた場合

E-mailと名前の設定

git config --global user.email example@example.com
git config --global user.name hogehoge

インストール

cd ./labelImg
brew install python3
pip3 install pipenv
pipenv --three

pipenv shell

pip install pyqt5 lxml

make qt5py3

rm -rf build dist

python setup.py py2app

cp -rf dist/labelImg.app /Applications

exit

参考

【labelImg:物体検出】公式READMEが当てにならない件
https://qiita.com/mdo4nt6n/items/46d53e19ed65c44c0eb7

インストール完了

こんな画面が表示されたらOK

※画像の部分は何も表示されなくてOK

image.png

次回以降の起動方法

デスクトップにアイコンができるらしい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LLDBでのデバッグまとめ

ある程度長いプログラムを書くようになってくると、エラーを探すのが難しくなってきます。

そんな時に必要となってくるのが「デバッガ」です。

多分これがないとやってられない、、???と思います。

ほぼ自分用ですが、LLDBの使い方まとめをやっていきます。

Macにはデフォルトで入っていそうな雰囲気なので、特に環境構築は必要ないかと思います。


今回、例として使うファイルはこんな感じです

test.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

void dainyu(int aa, int bb);

int main(void)
{
   double a = 3.0 ;
   double b = 5.0 ;
   double c = 7.0 ;

   dainyu(1,2);
   dainyu(2,3);
}
//--------------------------------------------------------
void dainyu(int aa, int bb){
    const int num1 = aa ;
    static int num2 = 0;
    num2 = 0;
    num2 += 2;

    printf("aa(const) :%d, bb(static) : %d \n", num1, num2);
}

conststaticの違いを実験するプログラムです。

ちなみに、出力は

aa(const) :1, bb(static) : 2 
aa(const) :2, bb(static) : 2 

となります。
つまり、staticにしておくと、関数が終了した後もnum2は値を保持し続けます。
つまり、static int num2 = 0;の初期化が行われるのは、初めてdainyu()が呼び出された際の一回のみです。

前準備

デバッグをするためには、コンパイルの際に-gを指定する必要があります。

gcc -g test.c

(蛇足ですが、最適化のための-O3と、警告をいっぱい出してくれるための-Wallは毎回入れた方がいいでしょう)

起動

先ほどのコンパイルでtestという名前の実行ファイルが出たとします。

起動するにはlldb [実行ファイル]のコマンドを打ちます。

lldb ./test
(lldb) target create "./test"
Current executable set to '/Users/K-eno/Scripts/test' (x86_64).

よく使うコマンド一覧

[実行]

(lldb) r

これをやると、シンプルに実行できます。

(lldb) r
Process 18038 launched: '/Users/KeigoEnomoto/Scripts/hydrodynamics/MPS/test' (x86_64)
aa(const) :1, bb(static) : 2 
aa(const) :2, bb(static) : 2 
Process 18038 exited with status = 0 (0x00000000) 

[コードの表示]

(lldb) l

数行ごとにtest.cのコードの中身を見ることができます。

[ブレイクポイントの設定]

行で指定

(lldb) b 15
Breakpoint 1: where = test`main + 32 at test.c:15:8, address = 0x0000000100000ec0

これで、15行目にブレイクポイントを打つことができました。
b の部分はbrとかbreakpointでもいいです
その後実行するとこんな感じです。

(lldb) r
Process 18105 launched: '/Users/KeigoEnomoto/Scripts/hydrodynamics/MPS/test' (x86_64)
Process 18105 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000ec0 test`main at test.c:15:8
   12   int main(void)
   13   {
   14   
-> 15   double a = 3.0 ;
   16   double b = 5.0 ;
   17   double c = 7.0 ;
   18   
Target 0: (test) stopped.

ちゃんと、15行目でストップできていますね。

関数名で指定

br set -n [funciton]

[function]の所に関数名を入れます。

実際に使ってみるとこんな感じです。

(lldb) br set -n dainyu
Breakpoint 1: where = test`dainyu + 14 at test.c:27:22, address = 0x0000000100000f0e
(lldb) r
Process 18177 launched: '/Users/KeigoEnomoto/Scripts/hydrodynamics/MPS/test' (x86_64)
Process 18177 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000f0e test`dainyu(aa=1, bb=2) at test.c:27:22
   24   }
   25   
   26   void dainyu(int aa, int bb){
-> 27       const int num1 = aa ;
   28       static int num2 = 0;
   29       num2 = 0;
   30       num2 += 2;
Target 0: (test) stopped.

関数の1行目まで入っていますね
その後ステップ実行すれば分かることですが、この場合、一つ目のdainyu(1,2)にブレイクポイントが打たれています。

ちなみに、breakpoint listで設定したブレイクポイントを全て出力することができます。

[ステップ実行]

ブレイクポイントを何処かに打った後にsもしくはn(next)を打つとステップごとに実行することができます。
s -c <count> のようにすると、ステップ回数を指定することができます。
その他のオプションはh sと打てば出てきます。

また、c(continue)を用いると、次のブレイクポイントまで(設定していない場合は最後まで)実行することができます。

[変数の表示]

frame varuableを使うと、変数の一覧とその中身を表示することができます。

(lldb) frame variable

実際にはこんな感じです。

(lldb) n
Process 18352 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100000eca test`main at test.c:17:8
   14   
   15   double a = 3.0 ;
   16   double b = 5.0 ;
-> 17   double c = 7.0 ;
   18   
   19   //printf("re : 5.0 , w(3) : %lf , w(7) : %lf ", weight(a,b), weight(c,b));
   20   //printf("a : %lf, b : %lf, c : %lf \n",a,b,c);
Target 0: (test) stopped.
(lldb) frame variable
(double) a = 3
(double) b = 5
(double) c = 0

まだ、16行目までしか実行はされていないので、cには7.0が代入されていません。

また、その後ろに変数を指定して見ることもできます。

(lldb) frame variable a
(double) a = 3


  • 変数の中身だけを見たい場合はpoコマンドを使っても実行できます。
(lldb) po b
5


  • 構造体を出力する際には、pコマンドを使用します。
(lldb) p *parameters
(simulation_parameters) $6 = {
  DIM = 2
  ParticleDistance = 0.025000000000000001
  FluidDensity = 1000
  KinematicViscosity = 9.9999999999999995E-7
  GRAVITY_SWITCH = 1
  DT = 0.001
  FINISH_TIME = 2
  OUTPUT_INTERVAL = 20
}

(これは、全く別のコードで使っている構造体ですが、、、、)


  • 配列の中身を見るときには少し特殊なコマンドを打つ必要があるようです。
      (他にいいのがあったら教えて欲しい、、、)
(lldb) p *(double (*)[10])Pressure
(double [10]) $1 = ([0] = 0, [1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, [6] = 0, [7] = 0, [8] = 0, [9] = 0)

これだと、Pressureという配列のうち、先頭から10個の値を見ることができます。

[終了]

qもしくはquitで終了することができます。

後書き

参考記事に非常に似通ってしまったので、パブリックにするかは悩みどころではありますが、いいメモにはなりました。
適宜、便利なコマンドは追加していこうと思います。

参考記事

LLDBを使ってCのデバッグをする
LLDBとかいう次世代高性能デバッガ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LLDBでのデバッグ備忘録

ある程度長いプログラムを書くようになってくると、エラーを探すのが難しくなってきます。

そんな時に必要となってくるのが「デバッガ」です。

多分これがないとやってられない、、???と思います。

ほぼ自分用ですが、LLDBの使い方まとめをやっていきます。

Macにはデフォルトで入っていそうな雰囲気なので、特に環境構築は必要ないかと思います。

ちなみに、ファイルを実行するたびに動作が異なる、、、、、、とかバグの原因が特定できない、、、、、、という時は、Valgrindを使うと、一発でメモリリークが分かって最高なのでぜひ試してみてください。
(LLDBでもメモリ系のコマンドがあるのでもしかしたらLLDBでも分かるかも??)


今回、例として使うファイルはこんな感じです

test.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

void dainyu(int aa, int bb);

int main(void)
{
   double a = 3.0 ;
   double b = 5.0 ;
   double c = 7.0 ;

   dainyu(1,2);
   dainyu(2,3);
}
//--------------------------------------------------------
void dainyu(int aa, int bb){
    const int num1 = aa ;
    static int num2 = 0;
    num2 = 0;
    num2 += 2;

    printf("aa(const) :%d, bb(static) : %d \n", num1, num2);
}

conststaticの違いを実験するプログラムです。

ちなみに、出力は

aa(const) :1, bb(static) : 2 
aa(const) :2, bb(static) : 2 

となります。
つまり、staticにしておくと、関数が終了した後もnum2は値を保持し続けます。
つまり、static int num2 = 0;の初期化が行われるのは、初めてdainyu()が呼び出された際の一回のみです。

前準備

デバッグをするためには、コンパイルの際に-gを指定する必要があります。

gcc -g test.c

(蛇足ですが、最適化のための-O3と、警告をいっぱい出してくれるための-Wallは毎回入れた方がいいでしょう)

起動

先ほどのコンパイルでtestという名前の実行ファイルが出たとします。

起動するにはlldb [実行ファイル]のコマンドを打ちます。

lldb ./test
(lldb) target create "./test"
Current executable set to '/Users/K-eno/Scripts/test' (x86_64).

よく使うコマンド一覧

[実行]

(lldb) r

これをやると、シンプルに実行できます。

(lldb) r
Process 18038 launched: '/Users/KeigoEnomoto/Scripts/hydrodynamics/MPS/test' (x86_64)
aa(const) :1, bb(static) : 2 
aa(const) :2, bb(static) : 2 
Process 18038 exited with status = 0 (0x00000000) 

[コードの表示]

(lldb) l

数行ごとにtest.cのコードの中身を見ることができます。

[ブレイクポイントの設定]

行で指定

(lldb) b 15
Breakpoint 1: where = test`main + 32 at test.c:15:8, address = 0x0000000100000ec0

これで、15行目にブレイクポイントを打つことができました。
b の部分はbrとかbreakpointでもいいです
その後実行するとこんな感じです。

(lldb) r
Process 18105 launched: '/Users/KeigoEnomoto/Scripts/hydrodynamics/MPS/test' (x86_64)
Process 18105 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000ec0 test`main at test.c:15:8
   12   int main(void)
   13   {
   14   
-> 15   double a = 3.0 ;
   16   double b = 5.0 ;
   17   double c = 7.0 ;
   18   
Target 0: (test) stopped.

ちゃんと、15行目でストップできていますね。

また、複数ファイルをコンパイルしている場合は、b main.c : 20のようにすると、main.cファイルの20行目にブレイクポイントを打つというように指定することができます。

関数名で指定

br set -n [funciton]

[function]の所に関数名を入れます。

実際に使ってみるとこんな感じです。

(lldb) br set -n dainyu
Breakpoint 1: where = test`dainyu + 14 at test.c:27:22, address = 0x0000000100000f0e
(lldb) r
Process 18177 launched: '/Users/KeigoEnomoto/Scripts/hydrodynamics/MPS/test' (x86_64)
Process 18177 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000f0e test`dainyu(aa=1, bb=2) at test.c:27:22
   24   }
   25   
   26   void dainyu(int aa, int bb){
-> 27       const int num1 = aa ;
   28       static int num2 = 0;
   29       num2 = 0;
   30       num2 += 2;
Target 0: (test) stopped.

関数の1行目まで入っていますね
その後ステップ実行すれば分かることですが、この場合、一つ目のdainyu(1,2)にブレイクポイントが打たれています。

ちなみに、breakpoint listで設定したブレイクポイントを全て出力することができます。

[ステップ実行]

ブレイクポイントを何処かに打った後にsもしくはn(next)を打つとステップごとに実行することができます。
s -c <count> のようにすると、ステップ回数を指定することができます。
その他のオプションはh sと打てば出てきます。

また、c(continue)を用いると、次のブレイクポイントまで(設定していない場合は最後まで)実行することができます。

[変数の表示]

frame varuableを使うと、変数の一覧とその中身を表示することができます。

(lldb) frame variable

実際にはこんな感じです。

(lldb) n
Process 18352 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100000eca test`main at test.c:17:8
   14   
   15   double a = 3.0 ;
   16   double b = 5.0 ;
-> 17   double c = 7.0 ;
   18   
   19   //printf("re : 5.0 , w(3) : %lf , w(7) : %lf ", weight(a,b), weight(c,b));
   20   //printf("a : %lf, b : %lf, c : %lf \n",a,b,c);
Target 0: (test) stopped.
(lldb) frame variable
(double) a = 3
(double) b = 5
(double) c = 0

まだ、16行目までしか実行はされていないので、cには7.0が代入されていません。

また、その後ろに変数を指定して見ることもできます。

(lldb) frame variable a
(double) a = 3


  • 変数の中身だけを見たい場合はpoコマンドを使っても実行できます。
(lldb) po b
5


  • 構造体を出力する際には、pコマンドを使用します。
(lldb) p *parameters
(simulation_parameters) $6 = {
  DIM = 2
  ParticleDistance = 0.025000000000000001
  FluidDensity = 1000
  KinematicViscosity = 9.9999999999999995E-7
  GRAVITY_SWITCH = 1
  DT = 0.001
  FINISH_TIME = 2
  OUTPUT_INTERVAL = 20
}

(これは、全く別のコードで使っている構造体ですが、、、、)


  • 配列の中身を見るときには少し特殊なコマンドを打つ必要があるようです。
      (他にいいのがあったら教えて欲しい、、、)
(lldb) p *(double (*)[10])Pressure
(double [10]) $1 = ([0] = 0, [1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, [6] = 0, [7] = 0, [8] = 0, [9] = 0)

これだと、Pressureという配列のうち、先頭から10個の値を見ることができます。

[終了]

qもしくはquitで終了することができます。

後書き

参考記事に非常に似通ってしまったので、パブリックにするかは悩みどころではありますが、いいメモにはなりました。
適宜、便利なコマンドは追加していこうと思います。

参考記事

LLDBを使ってCのデバッグをする
LLDBとかいう次世代高性能デバッガ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

macOSでASP .NET Coreを使ってAPIサーバをHerokuにデプロイする

macOSで.NET Coreを使ってAPIサーバをHerokuにデプロイするまでの手順をまとめます。

前提条件

実行環境は macOS Catalina です。
以下のツールはインストール済みとします。

  • VS Code
  • Docker

何を作るの?

シンプルなTodo管理APIサーバを構築します。データはデータベースに保存します。

.NET Core macOS版のインストール

Microsoftのウェブサイトからダウンロードしてインストールします。

必要な.NETツールをインストール

dotnet tool install --global dotnet-aspnet-codegenerator
dotnet tool install --global dotnet-ef

プロジェクトの作成

webapiテンプレートを使ってプロジェクトを作成します。
--no-httpsをつけないと、デフォルトでlocalhostの証明書を作る流れになってややこしくなります。

dotnet new webapi -o TodoApi --no-https

初期パッケージのインストール

Entity Framework Coreを使って、データベースへの書き込みを実現するので、以下のようにパッケージをインストールします。
ただ、今回はSQLiteだけ使うので、Microsoft.EntityFrameworkCore.Sqlite だけインストールしても問題ありません。

cd TodoApi
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.InMemory

初回起動

dotnet run で起動できます。

dotnet run

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/XXXXX/TodoApi

その後、curlを以下のように実行するとテンプレートの初期設定であるWeatherForecastの情報が取得できます。

curl http://localhost:5000/WeatherForecast

次のようなJSONが返されます。

[
   {
      "date" : "2020-08-01T08:48:53.47458+09:00",
      "summary" : "Mild",
      "temperatureC" : 39,
      "temperatureF" : 102
   },
   {
      "date" : "2020-08-02T08:48:53.474598+09:00",
      "summary" : "Mild",
      "temperatureC" : -20,
      "temperatureF" : -3
   },
   {
      "date" : "2020-08-03T08:48:53.474609+09:00",
      "summary" : "Scorching",
      "temperatureC" : 47,
      "temperatureF" : 116
   },
   {
      "date" : "2020-08-04T08:48:53.474619+09:00",
      "summary" : "Scorching",
      "temperatureC" : 54,
      "temperatureF" : 129
   },
   {
      "date" : "2020-08-05T08:48:53.474629+09:00",
      "summary" : "Sweltering",
      "temperatureC" : 39,
      "temperatureF" : 102
   }
]

モデルクラス TodoItem の追加

以下のようにTodoItem.csファイルをModelsフォルダの下に作成します。

TodoItem.cs
public class TodoItem
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

データベースコンテキストの追加

同様にModelsフォルダの下にTodoContext.csを作成します。

TodoContext.cs
using Microsoft.EntityFrameworkCore;

namespace TodoApi.Models
{
    public class TodoContext : DbContext
    {
        public TodoContext(DbContextOptions<TodoContext> options)
            : base(options)
        {
        }

        public DbSet<TodoItem> TodoItems { get; set; }
    }
}

Startup.cs を修正

Startup.csを以下のように修正します。ここで先ほど作成したモデルクラスやデータベースコンテキストを登録します。
今回は、SQLiteを使うのでそれ以外をコメントアウトしています。

Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

// 追加
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<TodoContext>(opt =>
                //opt.UseInMemoryDatabase("TodoList"));
                opt.UseSqlite("Filename=db.sqlite3"));  // 今回はこれを使う。 SQLiteのファイル名は db.sqlite3
                //opt.UseNpgsql("Host=localhost;Database=mydb;Port=15432;Username=user;Password=password")); // PosgreSQL
                //opt.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); // SQL Server

            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

コントローラを生成

必要なパッケージを追加します。

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.Design

コントローラTodoItemsControllerを生成します。

dotnet aspnet-codegenerator controller -name TodoItemsController -async -api -m TodoItem -dc TodoContext -outDir Controllers

この状態で自動的にTodoItemのCRUD APIが実装されます。

データベースマイグレーションとデータベースの更新

dotnet ef migrations add <どんな変更を加えたのかを表す任意の名称>という形でマイグレーションを作成します。
dotnet ef database updateでデータベースを更新します。

dotnet ef migrations add InitialCreate
dotnet ef database update

APIを試してみる

dotnet runでサーバを起動します。

POSTのテスト

curl -X POST \
     -H "Content-Type:application/json" \
     -d '{"Name":"Task 01", "IsComplete":false}' \
     http://localhost:5000/api/TodoItems

curl -X POST \
     -H "Content-Type:application/json" \
     -d '{"Name":"Task 02", "IsComplete":false}' \
     http://localhost:5000/api/TodoItems

GETのテスト:一覧取得

curl http://localhost:5000/api/TodoItems
[{"id":1,"name":"Task 01","isComplete":false},{"id":2,"name":"Task 02","isComplete":false}]

PUTのテスト

curl -X PUT \
     -H "Content-Type:application/json" \
     -d '{"Id":2, "IsComplete":true}' \
     http://localhost:5000/api/TodoItems/2

GETのテスト:特定レコードの取得

curl http://localhost:5000/api/TodoItems/2
{"id":2,"name":"Task 02","isComplete":true}

DELETEのテスト

curl -X DELETE \
     -H "Content-Type:application/json" \
     http://localhost:5000/api/TodoItems/2
{"id":2,"name":"Task 02","isComplete":true}

TodoItemにカラムを追加

以下のようにTodoItemCreatedAtを追加します。

TodoItem.cs
using System;  // 追加

public class TodoItem
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
    public DateTime CreatedAt { get; set; } // 追加
}

以下のようにマイグレーションを実行します。

dotnet ef migrations add AddDateColumn
dotnet ef database update

データベースが更新されたことを確認

sqlite db.sqlite3
sqlite> PRAGMA table_info('TodoItems');
0|Id|INTEGER|1||1
1|Name|TEXT|0||0
2|IsComplete|INTEGER|1||0
3|CreatedAt|TEXT|1|'0001-01-01 00:00:00'|0
sqlite> 

Herokuへデプロイ

以下のようなDockerfileを作ります。

Dockerfile
FROM mcr.microsoft.com/dotnet/core/aspnet

COPY bin/Release/netcoreapp3.1/publish/ App/
WORKDIR /App

CMD export ASPNETCORE_URLS=http://0.0.0.0:$PORT; dotnet TodoApi.dll

デプロイファイルを生成します。
bin/Release/netcoreapp3.1/ 配下にリリースようファイル群が生成されます。

dotnet publish -c Release

Herokuのインスタンスを作成して、Dockerfileをpush & releaseします。

heroku create 
heroku container:login
heroku container:push web --app <appName>
heroku container:release web --app <appName>

参考

チュートリアル: ASP.NET Core で Web API を作成する
https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/first-web-api?view=aspnetcore-3.1&tabs=visual-studio-code#prerequisites

チュートリアル: 移行機能の使用 - ASP.NET MVC と EF Core
https://docs.microsoft.com/ja-jp/aspnet/core/data/ef-mvc/migrations?view=aspnetcore-3.1

チュートリアル: JavaScript を使用して ASP.NET Core Web API を呼び出す
https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/web-api-javascript?view=aspnetcore-3.1

ASP.NET Core を使って Web API を作成する
https://docs.microsoft.com/ja-jp/aspnet/core/web-api/?view=aspnetcore-3.1

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む