😸

.NET 6 + React V18 + Typescriptで認証付きWebアプリのひな型を作る①枠組み作り

2022/11/06に公開約6,300字

はじめに

ASP.NET CoreとReactは、世界的にはポピュラーなフレームワークですが、組み合わせた場合の構築に関する情報が少ない(特に日本語)ことと、Visual Studioの自動生成テンプレートを使用するとTypeScriptが有効ではない、Reactのバージョンも古いなど、修正にもそれなりに手間がかかる内容になっています。

今回、ゼロからいろいろな資料をたどってASP.NET Core + React + TypeScriptでjwt認証機能付きWEBアプリケーションのひな型を作ったので、覚書として手順を残します。

サマリー

大まかな流れ(記事増やすすとともに随時更新)

  • 基本構成(本記事)
    • ソリューション+プロジェクトの枠組みを作る
    • クライアントとサーバーサイドで通信してサーバーサイドのデータを表示する
  • 認証機能の追加
    • サーバーサイドに認証機能の追加
    • クライアントサイドに認証機能の追加

環境

  • OS:Windows11
  • エディタ:VS Code
  • .NETバージョン:.NET6
  • Reactバージョン:18

コード管理(Github)

ソリューション・プロジェクトの構築

ソリューションを作る

まずはソリューションを作ります。今回は、「react-dotnet-skeleton」という名前のソリューション名で作ります。初めに、任意の場所に「react-dotnet-skeleton」という名前のフォルダを作った後、そこに移動して以下のコマンドを実行すると、ソリューションが生成されます

> cd [作りたい場所]
> dotnet new sln
結果の確認画像等(今後機会があったら更新)

サーバーサイド用のプロジェクトを作る

プロジェクト生成

次にサーバーサイド側のプロジェクトを作ります。
今回は、「webapi」のテンプレートを用いて、「serverapp」という名称で、フレームワークのバージョンは6.0で作るので、以下のコマンドをソリューションが入っているフォルダで実行します

> dotnet new webapi -n serverapp -f net6.0
結果の確認画像等(今後機会があったら更新)

ソリューションに追加

生成したプロジェクトをソリューションに追加します。こうすることで、Visual Studio(VS Codeではなく2019や2022等)でソリューションを開いたときにプロジェクト込みで開いたり、ソリューションフォルダで「dotnet build」コマンドを実行することで「server-app」のビルドも併せて実行などが行われるようになりなります。

> dotnet sln add serverapp/serverapp.csproj
結果の確認画像等(今後機会があったら更新)

実行して動作確認

生成したら、プロジェクトが格納されたフォルダに移動し、「dotnet run」又は「dotnet watch run」コマンドを実行することで、生成したプロジェクトを動かすことができます。

> cd serverapp
> dotnet run

ビルド成功後、ブラウザを立ち上げて「サーバーのurl+/WeatherForecast」をURLに打ち込むことで、自動生成時にあらかじめ用意されているAPIを動かすことができます。

クライアントのプロジェクトの構築

Create React Appを使用してテンプレートを生成する

Create React AppはFacebookが提供するReactフロントエンドのひな型を生成してくれるツールです。煩雑な環境設定や関連するパッケージ類のインポートなどの手間を省略して、初心者でも簡単にひな型を作ることができます。

今回は、Create React Appを使用して、ソリューションフォルダの直下に、「client-app」という名称で、TypeScript有りでひな型を作ります。
ソリューションがおかれているフォルダに移動してから、以下のコマンドを入力します。

> npx create-react-app clientapp --use-npm --template typescript

生成の成功後、ターミナルでclientappフォルダに移動して以下のコマンドを実行すればトップ画面を表示させることが可能になります。

> npm start

実行結果です。

サーバーサイドのデータをクライアントで表示できるようにする

プロジェクトの枠組みが出来たので、構築を進めていきます。
初めに、自動生成した状態のWEBAPIにあらかじめ組み込まれている「WeatherForecast」の結果を表示するように、クライアントのコードを変更していきましょう。
この表示を加えることで、まず認証機能を組み込む前にサーバーサイドとクライアントサイドで通信ができていることを確認出来るようになります。

変更の流れは以下です

  • サーバーサイド
    • Program.csを変更
  • クライアントサイド
    • App.tsxを変更
    • WeatherForecast.tsxを追加

サーバーサイドの構築

Program.csを変更

ASP.NET Coreは、初期状態だと別ドメインからのアクセスを制限する設定がされています。
元々はCSRF等の攻撃に対策するための機能ですが、今回のアプリではサーバーサイドとクライアントサイドでドメインが異なるので、初期設定のままだとクライアントからのアクセスも制限されてしまいます。
そのため、Program.csを変更して、別ドメインからのアクセスを許容する設定を行います。
(本記事では全開にしていますが、本番環境では特定のドメインからのアクセスのみを許容する様に変更する方が安全です。)

コード(ハイライト部分が変更点)

Program.cs
var builder = WebApplication.CreateBuilder(args);

+ string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();



+builder.Services.AddCors(o => o.AddPolicy(MyAllowSpecificOrigins, builder =>
+{
+    builder.AllowAnyOrigin()    // Allow CORS Recest from all Origin
+            .AllowAnyMethod()    // Allow All Http method
+            .AllowAnyHeader();   // Allow All request header
+}));


var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

+app.UseCors(MyAllowSpecificOrigins);   // Add For CORS

app.MapControllers();

app.Run();

クライアントサイドの構築

App.tsxを変更

元々の表示を消して、事項で説明する「<WeatherForecast />」を表示する様に変更します
コード(ハイライト部分が変更点)

App.tsx
import React from 'react';
import { WeatherForecast } from './WeatherForecast';

function App() {
  return (
    <div>
      <WeatherForecast />
    </div>
  );
}

export default App;

WeatherForecast.tsx追加

API:WeatherForecastのデータを表示するファイルを追加します

WeatherForecast.tsx(開くと表示)
WeatherForecast.tsx
import { useEffect, useState } from 'react';

interface Forecast {
  date: Date;
  temperatureC: number;
  temperatureF: number;
  summary: string;
}

export const WeatherForecast = () => {
    
    
    const [loading, setLoading] = useState(true);
    const [forecasts, setForecast] = useState<Forecast[]>();
  
    useEffect(() => {
        populateWeatherData();
    }, []);
  
    const populateWeatherData = async () => {
        const response = await fetch('https://localhost:5001/weatherforecast');
        const data = await response.json();
        setForecast(data);
        setLoading(false);
    };
    
    if(loading) return <div>loading....</div>

    return (
        <div>
            <h1 id="tabelLabel">Weather forecast</h1>
            <p>This component demonstrates fetching data from the server.</p>
            <table className="table table-striped" aria-labelledby="tabelLabel">
                <thead>
                <tr>
                    <th>Date</th>
                    <th>Temp. (C)</th>
                    <th>Temp. (F)</th>
                    <th>Summary</th>
                </tr>
                </thead>
                <tbody>
                {forecasts && forecasts.map((forecast) => (
                    <tr key={forecast.date.toString()}>
                    <td>{forecast.date.toString()}</td>
                    <td>{forecast.temperatureC}</td>
                    <td>{forecast.temperatureF}</td>
                    <td>{forecast.summary}</td>
                    </tr>
                ))}
                </tbody>
            </table>
        </div>
    )
}

実行結果

実行してみると、whetherforcastのデータをブラウザで表示することができます。

今回は以上です。

続きは次回です

Discussion

ログインするとコメントできます