LoginSignup
4
3

More than 5 years have passed since last update.

ASP.NET Core で Riot.js その2

Last updated at Posted at 2016-07-09

前回は Riot.js を使って Hello World してみました。
今回は Web API からデータを取得して一覧を表示するところをやってみます。

開発環境

Windows 10 Pro
Visual Studio Code

Server-side

Server-side に Web API を実装します。

今回作成する HTTP メソッドは以下となります。

API 概要 Request body Response body
GET /api/v1/person Person の一覧を取得する。 なし Personの一覧
GET /api/v1/person/{id} id に紐付く Person を取得する。 なし Person
POST /api/v1/person Person を作成する。 Person Person
PUT /api/v1/person/{id} id に紐付く Person を更新する。 Person なし
DELETE /api/v1/person/{id} id に紐付く Person を削除する。 なし なし

今回は一覧を表示するだけなので一番上の GET メソッドだけあればよいのですが、 CRUD 操作に必要なメソッドを一通り作成しておきます。まずは HTTPメソッドでやり取りするために必要な Model となるクラスを作成していきます。

Model

Model を作成していきます。今回は Building Your First Web API with ASP.NET Core MVC and Visual Studio を参考に Repository パターンを使ってやってみたいと思います。

ここでは以下のことをおこないます。

  • Model クラスの作成
  • Repository クラスの作成
  • 作成したRepository クラスの登録 (DI)

まずは Models フォルダに必要なファイルを作成します。Visual Studio Code のターミナルから以下のコマンドを入力してください。

mkdir Models & cd Models
yo aspnet:Class Person
yo aspnet:Interface IPersonRepository
yo aspnet:Class PersonRepository
cd ..

Model クラスの作成

Models フォルダに作成した Person.cs のコードを以下のように変更します。

Person.cs
namespace AspRiotApp.Models
{
    public class Person
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

Repository クラスの作成

Repository を作成します。

まずは IPersonRepository に Repository クラスで実装するデータアクセスロジックを定義します。Models フォルダに作成した IPersonRepository.cs のコードを以下のように変更します。

IPersonRepository.cs
using System.Collections.Generic;

namespace AspRiotApp.Models
{
    public interface IPersonRepository
    {
        void Add(Person person);
        IEnumerable<Person> GetAll();
        Person Find(string id);
        void Remove(string id);
        void Update(Person person);
    }
}

続いて作成したインターフェイスに定義されているデータアクセスロジックを PersonRepository に実装します。 Models フォルダに作成した PersonRepository.cs のコードを以下のように変更します。

PersonRepository.cs
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;

namespace AspRiotApp.Models
{
    public class PersonRepository : IPersonRepository
    {
         private static ConcurrentDictionary<string, Person> _people = 
              new ConcurrentDictionary<string, Person>();

        public PersonRepository()
        {
            Add(new Person { Name = "Eiji", Age = 35 });
            Add(new Person { Name = "Akemi", Age = 35 });
            Add(new Person { Name = "Satoru", Age = 7 });
            Add(new Person { Name = "Madoka", Age = 7 });
        }

        public IEnumerable<Person> GetAll() => _people.Values;

        public void Add(Person person)
        {
            person.Id = Guid.NewGuid().ToString();
            _people[person.Id] = person;
        }

        public Person Find(string id)
        {
            var person = default(Person);
            _people.TryGetValue(id, out person);
            return person;
        }

        public void Remove(string id)
        {
            var person = default(Person);
            _people.TryRemove(id, out person);
        }

        public void Update(Person person) => _people[person.Id] = person;
    }
}

実際は Database からデータを取得してマッピングしたりなどすると思いますが、今回はモックとして private な 静的メンバとして _people コレクションを用意して、適当なデータを突っ込んでいます。

private static ConcurrentDictionary<string, Person> _people = new ConcurrentDictionary<string, Person>();

System.Collections.Concurrent.ConcurrentDictionary は値のペアのコレクションです。値のペアからなるコレクションということで System.Collections.Generic.Dictionary と似ていますが ConcurrentDictionary は同時に複数のスレッドからアクセスすることができる(スレッド セーフ)という特徴を持っています。

作成したRepository クラスの登録

作成したRepository クラスをサービスとして登録します。ASP.NET Core プロジェクトで新しくサービスを登録する場合は Startup.cs に定義されている ConfigureServices() メソッドに処理を追加していきます。

Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddMvc();
    // リポジトリを登録
    services.AddSingleton<IPersonRepository, PersonRepository>();
}

AddSingleton() メソッドを呼んで作成した Repository クラスをひとつだけインスタンス化するよう DI コンテナに追加しています。ここで登録したサービスは Controller などのコンストラクタで受取ることで利用できます。

これで Model の作成は完了です。つぎは Controller を作成してWeb API として今回提供する HTTP メソッドを実装します。

Controller

ターミナルから以下のコマンドを入力して Controllers フォルダに PersonController.cs を作成します。

cd Controllers
yo aspnet:WebApiController PersonController
cd ..

作成したらコードを以下のように変更します。

PersonController.cs
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using AspRiotApp.Models;

namespace AspRiotApp.Controllers
{
    [Route("api/v1/[controller]")]
    public class PersonController : Controller
    {
        private IPersonRepository People { get; set; }

        public PersonController(IPersonRepository people)
        {
            People = people;
        }

        // GET api/v1/person
        [HttpGet]
        public IEnumerable<Person> Get() => People.GetAll();

        // GET api/v1/person/{id}
        [HttpGet("{id}", Name="GetPerson")]
        public IActionResult Get(string id)
        {
            var person = People.Find(id);

            if(person == null)
                return NotFound();

            return new ObjectResult(person);
        }

        // POST api/v1/person/
        [HttpPost]
        public IActionResult Post([FromBody]Person person)
        {
            if (person == null)
                return BadRequest();

            People.Add(person);

            return new CreatedResult("GetPerson", person);
        }

        // PUT api/v1/person/{id}
        [HttpPut("{id}")]
        public IActionResult Put(string id, [FromBody]Person person)
        {
            if (person == null || person.Id != id)
                return BadRequest();

            if (People.Find(id) == null)
                return NotFound();

            People.Update(person);

            return new NoContentResult();
        }

        // DELETE api/v1/person/{id}
        [HttpDelete("{id}")]
        public void Delete(string id) => People.Remove(id);
    }
}

Injection

コントラクタで先ほど登録した Repository サービスをインジェクションして受取ります。

private IPersonRepository People { get; set; }

public PersonController(IPersonRepository people)
{
    People = people;
}

Attribute

クラスに Attribute を付加してルーティングします。(api/v1/person)

[Route("api/v1/[controller]")]
public class PersonController : Controller
{
    ...
}

GET/POST/PUT/DELETE など Web API として今回提供する HTTP メソッドを定義するときは対応する Attribute をメソッドに付加します。

// GET api/v1/person
[HttpGet]
public IEnumerable<Person> Get() => People.GetAll();

付加できる Attribute は以下となります

  • HttpDelete
  • HttpGet
  • HttpHead
  • HttpOptions
  • HttpPatch
  • HttpPost
  • HttpPut

URLにパラメータを指定する場合は Attribute の引数に指定します。

// DELETE api/v1/person/{id}
[HttpDelete("{id}")]
public void Delete(string id) => People.Remove(id);

アクション名を指定する場合は Attribute の Name に指定します。

// GET api/v1/person/{id}
[HttpGet("{id}", Name="GetPerson")]
public IActionResult Get(string id)
{
    ...
}

戻り値

メソッドの戻り値には IEnumerable<Person> など任意の型を返すことができます。ステータスコードは 200 (OK) となります。

[HttpGet]
public IEnumerable<Person> Get() => People.GetAll();

また、 戻り値がないメソッドも扱えます。 この場合のステータスコードは 204 (No Content) となります。

[HttpDelete("{id}")]
public void Delete(string id) => People.Remove(id);

IActionResult は処理結果に応じてステータスコードを返す必要がある場合に使います。例として Put() メソッドでは条件によって BadRequest()NotFound() などそれぞれのステータスコードに対応するメソッドを呼び出しています。

 [HttpPut("{id}")]
 public IActionResult Put(string id, [FromBody]Person person)
 {
     if (person == null || person.Id != id)
         return BadRequest();

     if (People.Find(id) == null)
         return NotFound();

     People.Update(person);

     return new NoContentResult();
 }

Put() メソッドの条件とステータスコードは以下となります。

ステータスコード 条件
400 (Bad Request) 引数で与えられてる値に不正がある場合
404 (Not Found) 引数の id からデータを検索して一致するデータが見つからなかった場合
204 (No Content) 更新に成功した場合

最後にプロジェクト作成時にデフォルトで作成されている ValuesController.cs は使わないので削除すれば Server-side の実装は終わりです。続いて Client-side の実装をおこないます。

Client-side

Client-side では index.html と app.tag の修正をおこないます。

html

index.html は以下となります。

index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>ASP.NET Core で Riot.js</title>
    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
    <!-- app.tag の読込み -->
    <script src="app.tag" type="riot/tag"></script>
    <!-- riot+compiler.min.js の読込み -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/riot/2.5.0/riot+compiler.min.js"></script>
</head>

<body>
    <!-- app タグ -->
    <app></app>

    <!-- 作成したタグをマウントする -->
    <script>
        riot.mount('*');
    </script>
</body>

</html>

Web API とのやり取りを jQuery を使って Ajax 通信でおこないたいので jQuery を読込むようにします。

<script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>

あと前回 app タグの title 属性に値を入れていましたが今回は使わないので取りました。

<app></app>

Tag

app.tag に Ajax 通信で GET してレスポンス結果を一覧で表示する処理を実装します。

app.tag
<app>
    <h1>People</h1>
    <ul>
        <li each="{ person in people }" title="{ person.name }">
            <a href="#item/{person.id}">{ person.name } ({ person.age })</a>
        </li>
    </ul>

    <style scoped>
        :scope { display: block }
        ul {
            list-style: none;
            padding: 0;
            margin: 0;
        }
        li {
            margin: 0.8em 0;
        }
        a {
            background: #eee;
            border-radius:0.3em;
            color: inherit;
            display: block;
            padding: 1.2em;
            text-decoration: none;
        }
        a:hover {
            background: #7ACBE2
        }
    </style>

    // 一覧
    this.people = [];

    var self = this;

    // GET
    $.ajax({
        url  : '/api/v1/person',
        type : 'GET'
    })
    .then(
        function (result) {
            // 取得結果を一覧に設定
            self.people = result;
            // 更新
            self.update();
        },
        function () {
            console.log('失敗');
        }
    );
</app>

Ajax

$.ajax() で GET リクエストして people にレスポンス結果を設定しています。

// GET
$.ajax({
    url  : '/api/v1/person',
    type : 'GET'
})
.then(
    function (result) {
        // 取得結果を一覧に設定
        self.people = result;
        // 更新
        self.update();
    },
    function () {
        console.log('失敗');
    }
);

今回は $.ajax() のオプションに urltype だけ指定してますが、本番では timeout を指定したりする必要があると思います。また、Web API の内容を変更した場合など再度 GETリクエストしたときに IE などで取得結果がうまく反映されないことがあると思います。そのときは cache : false を指定してあげます。

each

ul タグ内では取得した結果を一覧で表示するために each="{ person in people }"people コレクションをループして要素を person として取り出して内容を li タグにバインドしています。

<li each="{ person in people }" title="{ person.name }">
    <a href="#item/{person.id}">{ person.name } ({ person.age })</a>
</li>

これで Client-side の実装は終わりです。dotnet run してブラウザから localhost に接続すると一覧が表示されます。

次回

次回は Server-side に実装した HTTP メソッドを利用して、Client-side でデータの追加や変更などができるようにします。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3