[VB.NET] REST API⑪ ビューからWeb APIにPUT(Controller実装・Viewの追加と実行)

2021年10月7日

WebAPIにPUTリクエストをおこない、データ更新をおこなうビューを追加してみましょう。
※ VisualStudio2019
このサンプルでは、[VB.NET] REST API⑩ ビューからWeb APIにPOST#1で作成したコントローラー(ApiTestController・ViewTestController)とモデルクラス(Recordクラス・ViewTestParamクラス)を使用しています。

コントローラーのEditメソッドを実装

まずは APIのコントローラー「ApiTestController」の「PutValue」メソッドを実装します。
本来はデータベースやファイルに書き込む処理を書くことになりますが、
このサンプルでは、Viewからリクエスト > Controllerで処理 > Viewで結果を表示 という一連の流れを確認するだけにしたいので、レスポンスとして成功のメッセージを返すだけの、ダミー処理です。

また、今回はPUTリクエストを処理するため、GETメソッド(GetValue)の実装もおこないます。
このメソッドは、ビューに表示するレコードを取得するために必要です。
今回のサンプルでは、指定されたidを持つダミーレコードを返しています。

コード

Imports System.Net
Imports System.Web.Http

Namespace Controllers
    Public Class ApiTestController
        Inherits ApiController

        ' GET: api/ApiTest
        Public Function GetValues() As IEnumerable(Of String)
            Return New String() {"value1", "value2"}
        End Function

        ' GET: api/ApiTest/5
        Public Function GetValue(ByVal id As Integer) As Record
            'レコード作成
            Dim rec As New Record
            rec.col1 = id
            rec.col2 = "This is a test record."
            rec.col3 = Now

            Return rec
            'Return "value"
        End Function

        ' POST: api/ApiTest
        Public Function PostValue(<FromBody()> ByVal value As Record) As System.Net.Http.HttpResponseMessage
            Debug.Print(String.Format("col1:{0}", value.col1))
            Debug.Print(String.Format("col2:{0}", value.col2))
            Debug.Print(String.Format("col3:{0}", value.col3))

            '登録処理(このサンプルはダミー処理)
            Dim tbl As New List(Of Record)
            tbl.Add(value)

            'レスポンスメッセージ作成
            Dim ret As System.Net.Http.HttpResponseMessage
            ret = New System.Net.Http.HttpResponseMessage(HttpStatusCode.OK)   '200

            '成功メッセージを設定
            Dim content As New Net.Http.StringContent("The record was created successfully !", Encoding.UTF8, "text/plain")
            ret.Content = content

            Return ret
        End Function

        ' PUT: api/ApiTest/5
        Public Function PutValue(ByVal id As Integer, ByVal value As Record) As System.Net.Http.HttpResponseMessage

            Dim tbl As New List(Of Record) From {New Record() With {.col1 = id, .col2 = "old data", .col3 = New Date(2020, 12, 1)}}

            '更新前の内容
            Debug.Print(String.Format("更新前の内容"))
            Debug.Print(String.Format("col1:{0}", tbl(0).col1))
            Debug.Print(String.Format("col2:{0}", tbl(0).col2))
            Debug.Print(String.Format("col3:{0}", tbl(0).col3))

            '更新処理(このサンプルはダミー処理)
            tbl(0).col1 = value.col1
            tbl(0).col2 = value.col2
            tbl(0).col3 = value.col3

            '更新後の内容
            Debug.Print(String.Format("更新後の内容"))
            Debug.Print(String.Format("col1:{0}", tbl(0).col1))
            Debug.Print(String.Format("col2:{0}", tbl(0).col2))
            Debug.Print(String.Format("col3:{0}", tbl(0).col3))

            'レスポンスメッセージ作成
            Dim ret As System.Net.Http.HttpResponseMessage
            ret = New System.Net.Http.HttpResponseMessage(HttpStatusCode.OK)   '200

            '成功メッセージを設定
            Dim content As New Net.Http.StringContent("The record was updated successfully !", Encoding.UTF8, "text/plain")
            ret.Content = content

            Return ret
        End Function

        ' DELETE: api/ApiTest/5
        Public Sub DeleteValue(ByVal id As Integer)

        End Sub
    End Class
End Namespace

続いてビューのコントローラー「ViewTestController」の「Edit」メソッドを実装します。
「ViewTest/Edit」は2つあります。

1つ目の 「ViewTest/Edit」は、ビューを表示する際に呼び出されます。
このメソッドでは、指定されたidのレコードをAPI(api/ApiTest/{id})から取得し、ビューに渡して表示しています。
注意点としては、Editの引数を「Optional ByVal id As Integer = -1」としていることろです。
こうすることで、idを省略してもメソッドが呼び出せるようになります。
ただし、idが省略された場合そのまま処理するわけにはいかないので、「BadRequest」応答を返しています。

2つ目の 「ViewTest/Edit」 はHttpPost属性が付いており、ビューからPOSTがおこなわれた際に呼び出されます。
ビューで入力した情報をFormCollectionから取り出してRecordクラスを作成し、JsonConvertを使ってJsonに変換した後、HttpClientとHttpResponseMessageを使用してAPIにPUTリクエストをおこなっています。

コード

Imports System.Web.Mvc

Namespace Controllers
    Public Class ViewTestController
        Inherits Controller

        ' GET: ViewTest
        Function Index() As ActionResult
            Return View()
        End Function

        ' GET: ViewTest/Details/5
        Function Details(ByVal id As Integer) As ActionResult
            Return View()
        End Function

        ' GET: ViewTest/Create
        Function Create() As ActionResult
            '画面パラメータ作成
            Dim model As New ViewTestParam
            model.Item1 = New List(Of Record) From {New Record()}
            model.Item2 = ""

            Return View(model)
        End Function

        ' POST: ViewTest/Create
        <HttpPost()>
        Function Create(ByVal collection As FormCollection) As ActionResult
            ' TODO: Add insert logic here

            'レコード作成するAPIのURL
            Dim url As String
            url = DirectCast(Request, System.Web.HttpRequestWrapper).Url.GetLeftPart(UriPartial.Authority) & "/api/ApiTest"

            'レコード作成
            Dim rec As New Record
            rec.col1 = collection("col1")
            rec.col2 = collection("col2")
            rec.col3 = New Date(2021, 1, 1)

            'レコードをシリアル化
            Dim json As String = ""
            json = Newtonsoft.Json.JsonConvert.SerializeObject(rec)

            'POST
            Dim result As System.Net.Http.HttpResponseMessage
            Dim clt As New Net.Http.HttpClient
            Dim content As New Net.Http.StringContent(json, Encoding.UTF8, "application/json")

            result = clt.PostAsync(url, content).Result

            '画面パラメータ作成
            Dim model As New ViewTestParam
            model.Item1 = New List(Of Record)
            model.Item1.Add(rec)

            'メッセージを取り出し
            model.Item2 = result.Content.ReadAsStringAsync.Result

            Return View(model)
        End Function

        ' GET: ViewTest/Edit/5
        Function Edit(Optional ByVal id As Integer = -1) As ActionResult

            'idが未指定の呼び出しの場合
            If (id = -1) Then
                Return New HttpStatusCodeResult(Net.HttpStatusCode.BadRequest)
            End If

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

            'GETを実行
            Dim clt As New Net.Http.HttpClient
            Dim result As New System.Net.Http.HttpResponseMessage

            result = clt.GetAsync(url).Result


            Dim model As New ViewTestParam
            Dim rec As Record
            If (result.StatusCode = Net.HttpStatusCode.OK) Then
                'Jsonを取り出し
                Dim json As String
                json = result.Content.ReadAsStringAsync.Result

                'デシリアライズ
                rec = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Record)(json)

                '画面パラメータ作成
                model.Item1 = New List(Of Record)
                model.Item1.Add(rec)
                model.Item2 = ""

            Else

                '画面パラメータ作成
                model.Item1 = New List(Of Record) From {New Record()}
                model.Item2 = ""
            End If

            Return View(model)
        End Function

        ' POST: ViewTest/Edit/5
        <HttpPost()>
        Function Edit(ByVal id As Integer, ByVal collection As FormCollection) As ActionResult
            ' TODO: Add update logic here

            'レコード作成するAPIのURL
            Dim url As String
            url = DirectCast(Request, System.Web.HttpRequestWrapper).Url.GetLeftPart(UriPartial.Authority) & "/api/ApiTest/" & id.ToString

            'レコード作成
            Dim rec As New Record
            rec.col1 = collection("col1")
            rec.col2 = collection("col2")
            rec.col3 = New Date(2021, 1, 1)

            'レコードをシリアル化
            Dim typ As Type = rec.GetType
            Dim dtslz As New Runtime.Serialization.Json.DataContractJsonSerializer(typ)
            Dim json As String = ""
            json = Newtonsoft.Json.JsonConvert.SerializeObject(rec)

            'PUTを実行
            Dim result As System.Net.Http.HttpResponseMessage
            Dim clt As New Net.Http.HttpClient
            Dim content As New Net.Http.StringContent(json, Encoding.UTF8, "application/json")

            result = clt.PutAsync(url, content).Result

            '画面パラメータ作成
            Dim model As New ViewTestParam
            model.Item1 = New List(Of Record)
            model.Item1.Add(rec)

            'メッセージを取り出し
            model.Item2 = result.Content.ReadAsStringAsync.Result

            Return View(model)

        End Function

        ' GET: ViewTest/Delete/5
        Function Delete(ByVal id As Integer) As ActionResult
            Return View()
        End Function

        ' POST: ViewTest/Delete/5
        <HttpPost()>
        Function Delete(ByVal id As Integer, ByVal collection As FormCollection) As ActionResult
            Try
                ' TODO: Add delete logic here

                Return RedirectToAction("Index")
            Catch
                Return View()
            End Try
        End Function
    End Class
End Namespace

ビュー(View)の追加

ビューを追加します。
ソリューションエクスプローラで、「Views」>「ViewTest」フォルダーを右クリックして
表示されるメニューから、「追加」>「ビュー」を選択します。

「MVC」>「表示」>「MVC5ビュー」を選択し、「追加」をクリックします。

ビュー名は「Edit」とし、テンプレートも「Edit」を選択します。
モデルクラスは「Record」を選択して、「追加」をクリックしてください。

「ViewTest」フォルダーに「Edit」という名前のビューが追加されます。
これで、「ViewTest/Edit」というビューと、それを処理する「ViewTestController」というコントローラーが用意できました。
ただ、このまま実行してもエラーになります。

追加したビュー(ViewTestのEdit.vbhtml)を変更して、パラメータを受け取れるようにします。
まずはMediaTypが「Record」になっているのを、「ViewTestParam」クラスに変更します。
メッセージ表示用に「message_label」というラベルを用意し、 「ViewTestParam」クラス の「Item2」の内容を赤文字で出力するようにします。
また、入力エリアは「ViewTestParam」クラス の「Item1」の内容を出力・検証するようにします。

コード

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

<h2>Edit</h2>

@*ViewTestControllerのEdit (Put) *@
@Using (Html.BeginForm("Edit", "ViewTest", FormMethod.Post))
    @Html.AntiForgeryToken()

    @<div class="form-horizontal">
    <h4>Record</h4>
    <hr />

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

    @Html.ValidationSummary(True, "", New With {.class = "text-danger"})
    <div class="form-group">
        @Html.LabelFor(Function(model) model.Item1.First.col1, htmlAttributes:=New With {.class = "control-label col-md-2"})
        <div class="col-md-10">
            @Html.EditorFor(Function(model) model.Item1.First.col1, New With {.htmlAttributes = New With {.class = "form-control"}})
            @Html.ValidationMessageFor(Function(model) model.Item1.First.col1, "", New With {.class = "text-danger"})
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(Function(model) model.Item1.First.col2, htmlAttributes:=New With {.class = "control-label col-md-2"})
        <div class="col-md-10">
            @Html.EditorFor(Function(model) model.Item1.First.col2, New With {.htmlAttributes = New With {.class = "form-control"}})
            @Html.ValidationMessageFor(Function(model) model.Item1.First.col2, "", New With {.class = "text-danger"})
        </div>
    </div>

    @*使用しないのでコメントにする*@
    @*
        <div class="form-group">
            @Html.LabelFor(Function(model) model.col3, htmlAttributes:=New With {.class = "control-label col-md-2"})
            <div class="col-md-10">
                @Html.LabelFor(Function(model) model.col3, New With {.htmlAttributes = New With {.class = "form-control"}})
                @Html.ValidationMessageFor(Function(model) model.col3, "", New With {.class = "text-danger"})
            </div>
        </div>
    *@

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Save" class="btn btn-default" />
        </div>
    </div>
</div>
End Using

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@Section Scripts
    @Scripts.Render("~/bundles/jqueryval")
End Section

実行して確認する

さっそく実行してみましょう。実行方法はこちらを参照してください。
まずは、id指定をせずに呼び出してみましょう。
アドレスバーに「/ViewTest/Edit」と入力してEnterキーを押してページを移動してみてください。
idが省略された場合はBadRequest応答をするようにしたので、 BadRequestと表示されます。

次は、idを指定して呼び出してみましょう。
アドレスバーに「/ViewTest/Edit/5」と入力してEnterキーを押してページを移動してみてください。
今度はEditページが表示されます。
col2を編集して「Save」ボタンをクリックします。

赤文字でメッセージが表示されたら、APIの呼び出し成功です。

出力ウィンドウも見てみましょう。
「ApiTestController」でリクエストの内容を取得できていることが確認できます。

─出力─────────────────────────
更新前の内容
col1:5
col2:old data
col3:2020/12/01 0:00:00
更新後の内容
col1:5
col2:This is a record update test.
col3:2021/01/01 0:00:00
────────────────────────────

ビューからPUTリクエストをおこない、APIで処理した結果をビューに表示させてみました。
今回のサンプルではリクエストの内容をコンソールに出力しただけですが、実際にはデータベースやファイルへの更新をおこなう処理を実装することになります。