[VB.NET] REST API⑭ OAuth2.0 認証#2(ビューから Web API を呼び出し)

2021年10月7日

前回で認証機能を実装したWeb APIをビューから呼び出してみましょう。
※ VisualStudio2019

ビューのコントローラーを実装

まずはビューのコントローラー「ViewTestController」のAPI呼び出し部分を作成していきます。
実装しているデフォルトのアクションは「Index」だけです。
テスト用に「GetToken」と「GetList」という独自のアクションを追加しています。
「GetToken」でAPIからアクセストークンを取得し、「GetList」でアクセストークンを付けてAPIを呼び出して一覧を取得します。

また、APIから取得したアクセストークンを保存するために「ApiToken」という内部クラスを定義しています。
「Models」フォルダ下にクラスを用意すればよかったのですが・・・まあいいか、とそのままにしています。
取得した「ApiToken」は一旦セッション変数に入れておき、一覧取得時にヘッダーのAuthorizationにセットしています。

コード

Imports System.Web.Mvc

Namespace Controllers
    Public Class ViewTestController
        Inherits Controller

        ''' <summary>
        ''' 認証トークン情報
        ''' </summary>
        Friend Class ApiToken
            ''' <summary>アクセストークン</summary>
            Property access_token As String

            ''' <summary>トークンタイプ</summary>
            Property token_type As String

            ''' <summary>有効期限(TimeSpan)</summary>
            Property expires_in As Long

            ''' <summary>取得した有効期限をDateTime型にしたもの</summary>
            Property expires As DateTime
        End Class

        ' GET: ViewTest
        Function Index() As ActionResult
            Session("token") = Nothing

            'モデル作成
            Dim model As New ViewTestParam With {.Item1 = New List(Of Record), .Item2 = ""}

            Return View(model)
        End Function

        ' POST: ViewTest/GetToken
        <HttpPost()>
        Function GetToken() As ActionResult
            Try
                'トークンを取得するAPIのURL
                Dim url As String
                url = DirectCast(Request, System.Web.HttpRequestWrapper).Url.GetLeftPart(UriPartial.Authority) & "/Token"

                'テスト用なのでユーザー/パスワードは固定
                Dim user As String = "user1"
                Dim pass As String = "password1"

                '認証コード
                Dim grant_type As String
                grant_type = String.Format("grant_type=password&username={0}&password={1}", user, pass)

                'コンテンツ作成
                Dim content As System.Net.Http.StringContent
                content = New System.Net.Http.StringContent(grant_type, New UTF8Encoding, "application/x-www-form-urlencoded")

                'リクエストメッセージ
                Dim req1 As System.Net.Http.HttpRequestMessage
                req1 = New System.Net.Http.HttpRequestMessage(Net.Http.HttpMethod.Post, Url)
                req1.Content = content


                Dim clt As New Net.Http.HttpClient
                Dim result As New System.Net.Http.HttpResponseMessage

                '認証コードを要求
                result = clt.SendAsync(req1).Result

                Dim model As New ViewTestParam
                If (result.StatusCode <> Net.HttpStatusCode.OK) Then
                    model.Item1 = New List(Of Record)
                    model.Item2 = "データの取得に失敗しました!" & result.Content.ReadAsStringAsync.Result

                    Return View("index", model)
                End If

                'Jsonを取り出し
                Dim json As String
                json = result.Content.ReadAsStringAsync.Result


                'トークン取得
                Dim _token As ApiToken
                _token = Newtonsoft.Json.JsonConvert.DeserializeObject(Of ApiToken)(json)

                '確認用にトークンの有効期限をDateTimeにしてみる
                _token.expires = Now.AddSeconds(_token.expires_in)

                'Session変数に保管
                Session("token") = _token


                'モデル作成
                model.Item1 = New List(Of Record)
                model.Item2 = String.Format("アクセストークンを取得しました! (有効期限:{0}秒)", _token.expires_in)

                Return View("index", model)
            Catch
                Return View()
            End Try
        End Function

        ' POST: ViewTest/GetList
        Function GetList() As ActionResult
            Try
                Dim model As New ViewTestParam
                If IsNothing(Session("token")) Then

                    'モデル作成
                    model.Item1 = New List(Of Record)
                    model.Item2 = "アクセストークンを取得してください。"
                    Return View("index", model)
                End If

                'トークン
                Dim _token As ApiToken
                _token = CType(Session("token"), ApiToken)

                '一覧を取得するAPIのURL
                Dim url As String
                url = DirectCast(Request, System.Web.HttpRequestWrapper).Url.GetLeftPart(UriPartial.Authority) & "/api/ApiTest"

                Dim clt2 As New Net.Http.HttpClient
                Dim result2 As New System.Net.Http.HttpResponseMessage
                '認証ヘッダーにアクセストークンを設定
                clt2.DefaultRequestHeaders.Authorization = New Net.Http.Headers.AuthenticationHeaderValue("Bearer", _token.access_token)

                'GETを実行
                result2 = clt2.GetAsync(Url).Result


                If (result2.StatusCode = Net.HttpStatusCode.OK) Then
                    'Jsonを取り出し
                    Dim json As String
                    json = result2.Content.ReadAsStringAsync.Result

                    'デシリアライズ
                    Dim rec_list As List(Of Record)
                    rec_list = Newtonsoft.Json.JsonConvert.DeserializeObject(Of List(Of Record))(Json)

                    'モデル作成
                    model.Item1 = rec_list
                    model.Item2 = "データを取得しました!"

                ElseIf (result2.StatusCode = Net.HttpStatusCode.Unauthorized) Then
                    '認証トークン有効期限切れ
                    'モデル作成
                    model.Item1 = New List(Of Record)
                    model.Item2 = "アクセストークンを取得してください!" & result2.Content.ReadAsStringAsync.Result

                Else
                    'モデル作成
                    model.Item1 = New List(Of Record)
                    model.Item2 = "データの取得に失敗しました!" & result2.Content.ReadAsStringAsync.Result
                End If

                Return View("index", model)
            Catch
                Return View()
            End Try

        End Function

    End Class
End Namespace

ビューを実装

ビューを実装していきます。
といっても、「GetToken」アクションを呼び出すボタンと、「GetList」アクションを呼び出すボタンを追加するだけです。

コード

@ModelType ViewTestParam
@Code
    ViewData("Title") = "index"
End Code

<h2>index</h2>


@Html.Label("message_label", Model.Item2, New With {.style = "color:red;"})

<hr />

@Using (Html.BeginForm("GetToken", "ViewTest", FormMethod.Post))
    @<div class="form-horizontal">
        <h4>Token</h4>

        <div class="form-group">
            <input type="submit" value="GetToken" class="btn btn-default" />
        </div>
    </div>
End Using

@Using (Html.BeginForm("GetList", "ViewTest", FormMethod.Get))
    @<div class="form-horizontal">
        <h4>GetList</h4>
        <hr />
        <div class="form-group">
            <input type="submit" value="GetList" class="btn btn-default" />
        </div>
    </div>
End Using

<hr />

<table class="table">
    <tr>
        <th>
            @*最初の要素が無くてもエラーにならないので心配しなくてOK*@
            @Html.DisplayNameFor(Function(model) model.Item1.First().col1)
        </th>
        <th>
            @Html.DisplayNameFor(Function(model) model.Item1.First().col2)
        </th>
        <th>
            @Html.DisplayNameFor(Function(model) model.Item1.First().col3)
        </th>
        <th></th>
    </tr>

    @For Each item In Model.Item1
        @<tr>
            <td>
                @Html.DisplayFor(Function(modelItem) item.col1)
            </td>
            <td>
                @Html.DisplayFor(Function(modelItem) item.col2)
            </td>
            <td>
                @Html.DisplayFor(Function(modelItem) item.col3)
            </td>
            <td>
                @*@Html.ActionLink("Edit", "Edit", New With {.id = item.PrimaryKey}) |
                    @Html.ActionLink("Details", "Details", New With {.id = item.PrimaryKey}) |
                    @Html.ActionLink("Delete", "Delete", New With {.id = item.PrimaryKey})*@
            </td>
        </tr>
    Next

</table>

実行して確認する

さっそく実行してみましょう。実行方法はこちらを参照してください。

アドレスバーに「/ViewTest/index」と入力してEnterキーを押してページを移動してから、「GetToken」をクリックしてアクセストークンを取得します。

アクセストークンの取得に成功すると、メッセージとトークンの有効期限が表示されます。
トークンの有効期限が切れる前に「GetList」をクリックして一覧を取得してみましょう。

一覧の取得に成功すると、メッセージと取得した一覧が表示されます。

1分後(有効期限が切れた後)、もう一度「GetList」をクリックしてみましょう。
要求が拒否されたメッセージが表示され、トークンの有効期限がちゃんと機能していることが確認できます。
再度「GetToken」をクリックしてアクセストークンを取得しなおせば、一覧の取得ができるはずです。

OAuth認証(Bearer Token)を実装したWeb APIをビューから呼び出してみました。
いずれリフレッシュトークンの実装についてもやってみようと思います。