前回までで、画面表示を元にテーブル設計を行いました。
今回からはテーブル基本設計を元にデータ型と、データ型に沿ったデータを返すwebapiを作っていきます。
ガイド
全体Index:タスク管理ツールをReact + ASP.Coreで作る - Index
今回の記事でやること
いきなりDBを構築するところまで手を広げると動作確認するまでの工程が長くなってしまうので、まずは以下をやっていきます
- サーバーサイド
- データモデルを元にモデルクラスを作る
- ダミーデータを返すAPIを作る
- クライアントサイド
- APIと通信してデータを取得するコンポーネントを作る
- コンポーネントをメイン画面で読みだし、APIの結果をクライアントサイドのメイン画面に表示する
それでは始めていきましょう
#サーバーサイド
初めにサーバーサイドです。
サーバーサイドでは以下の2ファイルを作ります。
データモデルを元にモデルクラスを作る
前回構築したデータモデルは以下になります。
Guidは簡単に説明すると他と重複せず一意の値として取得できるアルゴリズムで生成された識別子です。ランダムに生成されて他と重複しない文字列という認識でまずはOKです
まずはサーバーサイドプログラムにモデルクラスを作りましょう。
モデルクラスはサーバーサイドプログラムにおいてデータ構造を定義するとともに、データベースのテーブル定義を作るための元ネタとして機能します。
ではモデルクラスを作っていきましょう。
モデルクラスはデータモデルの定義をメンバ名にしたクラスです。データモデルを元に、以下の様になります。
using System;
namespace server_app.Models.EDM
{
public partial class t_task
{
public Guid id_task { get; set; }
public string title { get; set; }
public bool is_finish { get; set; }
public DateTime? end_date_scheduled { get; set; }
}
}
シンプルですね。
ちなみにこれがデータベースのテーブル定義の元ネタだとするなら、主キーとかどうなるの?というところも気になりますが、次回以降でコンテキストクラスというものを作るときに登場します。
モデルクラスが出来たので、次はwebapiを作ります
ダミーデータを返すAPIを作る
ASP.NET Coreのフレームワークでは、Web APIはControllerBaseというクラスを派生させて作るのが基本的なアプローチみたいです。
コントローラークラスはクライアントからの要求を受け取り、サーバーサイドで様々な処理を行って結果をクライアントに返すというのが主要な役割とのことです。
まさにクライアントから見るとAPIそのものですね。
コントローラークラスは以下のような内容になります。
最初に作ったテンプレートに含まれている、「WeatherForecastController」を元にして、「t_task」クラスの配列を返すように変更したものです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using server_app.Models.EDM;
namespace server_app.Controllers
{
[ApiController]
[Route("[controller]")]
public class TaskController : ControllerBase
{
private readonly ILogger<TaskController> _logger;
public TaskController(ILogger<TaskController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<t_task> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new t_task
{
id_task = Guid.NewGuid(),
title = "item_" + index.ToString(),
is_finish = true,
end_date_scheduled = DateTime.Now.AddDays(index)
})
.ToArray();
}
}
}
上記のコードを保存した後、F5押下または以下のコマンドを実行してサーバーサイドプログラムを実行し、その後ブラウザでapiにアクセスすると、以下の様に作ったwebapiからの出力を得ることができます。
> dotnet run
クライアントサイドでの表示
次はクライアントサイドです。
クライアントサイドでは、以下の2ファイルを追加・修正します。(TaskList.tsxを新設、App.tsxを修正)
内容は、APIにアクセスしてデータを取得し、テーブル形式で表示するコンポーネント「TaskList.tsx」の作成と、「App.tsx」内でTaskList.tsxの呼び出しの変更です。
APIと通信してデータを取得するコンポーネントを作る
「TaskList.tsx」も、サーバーサイド同様「WeatherForecast」をテーブルで表示するコンポーネントと同じ流れで、アクセスするデータやAPIのURLなどを変えた内容となっています。
import { useEffect, useState } from 'react';
interface Task {
id_task: string;
title: string;
is_finish: boolean;
end_date_scheduled: Date;
}
export const TaskList = () => {
const [loading, setLoading] = useState(true);
const [tasks, setTasks] = useState<Task[]>();
useEffect(() => {
populateWeatherData();
}, []);
const populateWeatherData = async () => {
const response = await fetch('https://localhost:5001/task');
const data = await response.json();
setTasks(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>No.</th>
<th>Fin.</th>
<th>Title</th>
<th>Due Date</th>
</tr>
</thead>
<tbody>
{tasks && tasks.map((task, index) => (
<tr key={task.id_task}>
<td>{index+1}</td>
<td><input type="checkbox" defaultChecked={task.is_finish} disabled /></td>
<td>{task.title}</td>
<td>{task.end_date_scheduled?.toString()}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
コンポーネントをメイン画面で読みだし、APIの結果をクライアントサイドのメイン画面に表示する
「App.tsx」で読みだすコンポーネントを先ほど作成した「TaskList.tsx」に変更します。
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { WeatherForecast } from './WeatherForecast';
import { TaskList } from './TaskList';
function App() {
return (
<div>
- <WeatherForecast />
+ <TaskList />
</div>
);
}
export default App;
サーバーサイドが動いている状態で、クライアントサイドも動かすと、以下のような表示を得ることができます。
データモデルに従い、ダミーですがサーバーサイドでデータを返すAPIと、APIからデータを受け取ってクライアントサイドで表示を行うという一連の機能が出来ました。
続きは次回です。
おさらい
開くと表示
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using server_app.Models.EDM;
namespace server_app.Controllers
{
[ApiController]
[Route("[controller]")]
public class TaskController : ControllerBase
{
private readonly ILogger<TaskController> _logger;
public TaskController(ILogger<TaskController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<t_task> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new t_task
{
id_task = Guid.NewGuid(),
title = "item_" + index.ToString(),
is_finish = true,
end_date_scheduled = DateTime.Now.AddDays(index)
})
.ToArray();
}
}
}
import { useEffect, useState } from 'react';
interface Task {
id_task: string;
title: string;
is_finish: boolean;
end_date_scheduled: Date;
}
export const TaskList = () => {
const [loading, setLoading] = useState(true);
const [tasks, setTasks] = useState<Task[]>();
useEffect(() => {
populateWeatherData();
}, []);
const populateWeatherData = async () => {
const response = await fetch('https://localhost:5001/task');
const data = await response.json();
setTasks(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>No.</th>
<th>Fin.</th>
<th>Title</th>
<th>Due Date</th>
</tr>
</thead>
<tbody>
{tasks && tasks.map((task, index) => (
<tr key={task.id_task}>
<td>{index+1}</td>
<td><input type="checkbox" defaultChecked={task.is_finish} disabled /></td>
<td>{task.title}</td>
<td>{task.end_date_scheduled?.toString()}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
import { useEffect, useState } from 'react';
interface Task {
id_task: string;
title: string;
is_finish: boolean;
end_date_scheduled: Date;
}
export const TaskList = () => {
const [loading, setLoading] = useState(true);
const [tasks, setTasks] = useState<Task[]>();
useEffect(() => {
populateWeatherData();
}, []);
const populateWeatherData = async () => {
const response = await fetch('https://localhost:5001/task');
const data = await response.json();
setTasks(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>No.</th>
<th>Fin.</th>
<th>Title</th>
<th>Due Date</th>
</tr>
</thead>
<tbody>
{tasks && tasks.map((task, index) => (
<tr key={task.id_task}>
<td>{index+1}</td>
<td><input type="checkbox" defaultChecked={task.is_finish} disabled /></td>
<td>{task.title}</td>
<td>{task.end_date_scheduled?.toString()}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { WeatherForecast } from './WeatherForecast';
import { TaskList } from './TaskList';
function App() {
return (
<div>
- <WeatherForecast />
+ <TaskList />
</div>
);
}
export default App;