はじめてのASP.Net Razor Pages(ホットペッパーAPIを使ってみる編)

前回

https://qiita.com/Pir0/items/da4f31bc57e41c305951

<h1>はじめに</h1>
ホットペッパーの店名サーチAPIを用いると飲食店情報を簡単に取得することができます。今回はこれを使用して鳥貴族検索アプリを作成します。

https://webservice.recruit.co.jp/doc/hotpepper/guideline.html

<h1>開発環境</h1>
・Windows10 Home x64

・VisualStudio2022
・ASP.net 6.0

<h1>開発の準備</h1>
ホットペッパーAPIを使用するためにAPIキーを取得する必要があります。
以下のURLよりメールアドレスの登録を行い、APIキーを取得します。

https://webservice.recruit.co.jp/register/

登録したメールアドレスに以下のようなメールが送られます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954876/42186f8c-4371-a7aa-a519-02ff86a5cec4.png)

このAPIキーを控えておいてください。

<h1>プロジェクトの編集</h1>
いよいよソースコードをいじっていきます。

今回手を加えるのは以下のファイルです。

1. Pages\HotPepper\Index.cshtml (新規作成)
2. Pages\HotPepper\Index.cshtml\Index.cshtml.cs (新規作成)
3. Pages\Shared\ _Layout.cshtml
4. appsettings.json

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954876/51863297-6df4-1d0a-4ae4-3970689f50d1.png)



```html:Pages\HotPepper\Index.cshtml
@page
@model IndexModel
@{
    ViewData["Title"] = "HotPepper";
}

<h1>@ViewData["Title"]</h1>

<a style="color:red">※β版のため鳥貴族しか検索できません！</a>

<div style="display:flex">
    <form method="post">
        <input type="text" asp-for="SearchString" />
        <button type="submit" asp-page-handler="search" class="btn btn-secondary">検索</button>
    </form>
    <a>@Model.errMsg</a>
</div>

<table class="table">
    <thead>
        <tr>
            <th>店舗名</th>
            <th>住所</th>
            <th>詳細</th>
        </tr>
    </thead>
    <tbody>
        @for (int i = 0; i < Model.nameList.Count; i++)
        {
            <tr>
                <td>@Html.DisplayFor(modelItem => @Model.nameList[i])</td>
                <td>@Html.DisplayFor(modelItem => @Model.addressList[i])</td>
                <td><a href=@Model.urlList[i] target="_blank">移動</a></td>
            </tr>
        }
    </tbody>
</table>
<a href="http://webservice.recruit.co.jp/"><img src="http://webservice.recruit.co.jp/banner/hotpepper-s.gif" alt="ホットペッパー Webサービス" width="135" height="17" border="0" title="ホットペッパー Webサービス"></a>
```

```C#:Pages\HotPepper\Index.cshtml\Index.cshtml.cs
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;
using System.Net.Http;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Configuration;

namespace SampleWebApp01.Pages.HotPepper
{
    public class IndexModel : PageModel
    {

        [BindProperty]
        public string SearchString { get; set; }

        public IEnumerable<string> name { get; set; }
        public List<string> nameList { get; set; }
        public IEnumerable<string> address { get; set; }
        public List<string> addressList { get; set; }
        public IEnumerable<string> urls { get; set; }
        public List<string> urlList { get; set; }
        public IEnumerable<int> code { get; set; }
        public IEnumerable<string> msg { get; set; }
        public string errMsg { get; set; }


        private readonly IConfiguration _conf;

        public IndexModel(IConfiguration configuration)
        {
            _conf = configuration;
        }

        public void OnGet()
        {
            nameList = new List<string>();
        }

        public async Task OnPostSearchAsync()
        {
            //appsettingsからAPIキーを取得
            var API_KEY = _conf.GetSection("HotPepper")["Key"];
            string url = "http://webservice.recruit.co.jp/hotpepper/shop/v1/?key=" + API_KEY + "&keyword=鳥貴族 " + SearchString +"&format=json";

            using (var client = new HttpClient())
            {
                // リクエストの内容に従って、指定したURLにGetメソッドでリクエスト。
                var httpsResponse = await client.GetAsync(url);
                // サーバーからのレスポンスをテキスト形式で受け取り。
                var responseContent = await httpsResponse.Content.ReadAsStringAsync();
                //jsonをShopInfoクラスにデシリアライズ
                var shopInfo = System.Text.Json.JsonSerializer.Deserialize<ShopInfo>(responseContent);

                try
                {
                    //ShopInfoにデシリアライズしたデータを参照可能なグローバル変数に格納
                    name = shopInfo.results.shop.Select(s => s.name);
                    nameList = name.ToList();
                    address = shopInfo.results.shop.Select(s => s.address);
                    addressList = address.ToList();
                    urls = shopInfo.results.shop.Select(s => s.urls.pc);
                    urlList = urls.ToList();
                }
                catch (Exception e)
                {
                    nameList = new List<string>();
                    //エラーメッセージ生成
                    code = shopInfo.results.error.Select(s => s.code);
                    var displayCode = code.ElementAt(0);
                    msg = shopInfo.results.error.Select(s => s.message);
                    var displayMsg = msg.ElementAt(0);
                    errMsg = "　検索エラー：" + displayMsg;
                    Console.Write(errMsg);
                }
            }
        }

        public class Genre
        {
            public string name { get; set; }
        }

        public class Results
        {
            public string api_version { get; set; }
            public int results_available { get; set; }
            public string results_returned { get; set; }
            public int results_start { get; set; }
            public List<Shop> shop { get; set; }
            public List<Error> error { get; set; }
        }

        public class ShopInfo
        {
            public Results results { get; set; }
        }

        public class Shop
        {
            public string address { get; set; }
            public string desc { get; set; }
            public Genre genre { get; set; }
            public string id { get; set; }
            public string name { get; set; }
            public string name_kana { get; set; }
            public Urls urls { get; set; }
        }

        public class Urls
        {
            public string pc { get; set; }
        }
        public class Error
        {
            public int code { get; set; }
            public string message { get; set; }
        }
    }
}
```
```html:Pages\Shared\ _Layout.cshtml
//~~省略~~//
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
    <ul class="navbar-nav flex-grow-1">
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
        </li>
//~~追記部分~~
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/HotPepper/Index">HotPepper</a>
        </li>
//~~追記部分ここまで~~
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
        </li>
    </ul>
</div>
//~~省略~~//
```
```json:appsettings.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
//~~追記部分~~
  "HotPepper": {
    "Key": "自身のAPIキー"
  },
//~~追記部分ここまで~~
  "AllowedHosts": "*"
}


```

<h1>動かしてみる</h1>
編集したプロジェクトを実行してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954876/fbf922c7-727a-96b7-b942-e9fbc457b5d0.png)

ヘッダーにHotPepperが追加されているので押してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954876/5c92b6da-b63f-b85e-ea89-b0e03c61edff.png)

先ほど作成した画面が出てきました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954876/e941176e-83d3-0c99-1882-1758f4832341.png)

なにかワードを入れてみましょう。
池袋と入力し、検索してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954876/313b93c3-0703-a451-14e3-8e23cb7314f2.png)

検索できました。
データテーブルに成形されており、見栄えもきれいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954876/7d9def26-7270-2fc0-3727-75c8383d444b.png)

お疲れさまでした。最後に参考にさせていただいた記事を載せます。

<h1>参考にした記事</h1>
JSONの扱い方

https://qiita.com/hqf00342/items/6dd97a99c5c46c14e0e4

https://yaspage.com/cs-json-serialize/#toc2

配列の扱い方

https://programming.pc-note.net/csharp/linq_method.html

https://mebee.info/2022/01/03/post-36312/

キー管理

https://www.ipentec.com/document/csharp-asp-net-using-appsetting-json-in-asp-net-razor-pages-application