かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

ASP.NET WebAPIでAPIを作ってJavaScriptから呼ぶまで

ということで、タイトル通りのことをしてみようと思います。空のASP.NETのプロジェクトからいろいろ足していく形でやろうと思います。

プロジェクトの作成とAPIの作成

まず、空のASP.NETのプロジェクトを作ります。

f:id:okazuki:20140102152258p:plain

ASP.NET WebAPIに必要なアセンブリを追加します。特に縛りがない限りは最新を利用したほうがいいのでNuGetからMicrosoft ASP.NET Web API 2 Web Hostをインストールします。ぱっと見た感じ以下のアセンブリが参照に追加されました。

  • Nwetonsoft.Json
  • System.Net.Http
  • System.Net.Http.Formatting
  • System.Web.Http
  • System.Web.Http.WebHost

次にプロジェクトに以下のものを追加します。

  • プロジェクトの右クリックメニューから「追加」→「フォルダ」を選びControllersという名前のフォルダを作成
  • Controllerフォルダの右クリックメニューから「追加」→「クラス」でHelloWorldControllerという名前のクラスを作成

WebAPIのコントローラはApiControllerクラスを継承しないといけないので継承します。

ここまでのコードを以下に示します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http; // こいつの追加を忘れずに

namespace HelloWebApi.Controllers
{
    public class HelloWorldController : ApiController
    {

    }
}

次に、コントローラを作っただけでは呼ばれないのでAPIが呼ばれるようにルーティングの設定を行います。Global.asax(グローバル アプリケーション クラス)を作成して、Application_Startメソッドに以下の定義を追加します。

using System.Web.Http; // これを頭に追加


protected void Application_Start(object sender, EventArgs e)
{
    GlobalConfiguration.Configure(config =>
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional });
        });
}

routeTemplateで使用している{controller}や{id}は予約語みたいなもので、コントローラ名やURLに含まれるリソースを一意に識別するためのidを指定するためのプレースホルダーみたいなものだと思っておけばOKです。自分でいろいろ細かくカスタマイズすることもできます。詳しくは味噌先生の以下の記事を参照してみてください。

プロジェクトの新規作成のときにWebAPIを使うようにしている場合は、参照設定や、ルートの設定はすでにされています。Application_StartメソッドからWebApiConfigクラスのメソッドが呼ばれていて、そのなかで上記のようなコードが記載されているはずなので確認してみてください。

APIを作ってブラウザから叩いてみよう

APIのControllerクラスは作りましたが、中身がからっぽなので、簡単に以下のような記述を足してGETメソッドに対応するものを作ります。

// /api/HelloWorld
public string Get()
{
    return "Hello world";
}

実行してブラウザのURLでhttp://localhost:ポート番号/api/HelloWorldというURLにアクセスすると以下のようなJSONファイルをダウンロードできるはずです。

"Hello world"

では、ブラウザから叩いてみます。生のJavaScriptから叩くのは正直つらいのでNuGetからjQueryをインストールします。プロジェクトにDefualt.htmlという名前のHTMLファイルを作成して、以下のように書きます。jQueryのajaxメソッドで単純にHelloWorldのAPIを叩いて結果をspanの中に流し込んでいます。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <h1>Hello WebAPI</h1>

    <div>
        <!-- ボタンを押すとAPIを呼ぶ -->
        <button onclick="callHelloWorld()">GET /api/HelloWorld</button>
        <br/>
        <span id="helloWorldResultPlaceHolder">ここにHelloWorldAPIの結果が入ります</span>
    </div>

    <script src="Scripts/jquery-2.0.3.min.js"></script>
    <script type="text/javascript">
        function callHelloWorld() {
            // jQueryを使って呼ぶだけ
            $.ajax("/api/HelloWorld").then(function (r) {
                $("#helloWorldResultPlaceHolder").text(r);
            });
        }
    </script>
</body>
</html>

実行すると、以下のような画面が表示されます。

f:id:okazuki:20140102163620p:plain

ボタンを押してしばらくすると、APIから返された文字列が画面に表示されます。

f:id:okazuki:20140102163722p:plain

POSTのメソッドも作ってみよう

次にPOSTのメソッドを受け取るAPIを作ってみます。こいつは、フォームのテキストボックスに入力した内容を送って、それにHello worldをくっつけて返すものにします。POSTで単純に文字列の引数を受け取るだけの方法がちょっとわからなかったので妥協してオブジェクトを受け取るようにしました・・・。

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;

namespace HelloWebApi.Controllers
{
    public class HelloWorldController : ApiController
    {
        // /api/HelloWorld
        public string Get()
        {
            return "Hello world";
        }

        // POST /api/HelloWorld
        public string Post(Data data)
        {
            return data.Name + " Hello world";
        }
    }

    /// <summary>
    /// JSONのデータの入れ物
    /// </summary>
    public class Data
    {
        public string Name { get; set; }
    }
}

JavaScriptの呼び出し側は以下の通り。

<div>
    <input type="text" id="inputText" />
    <button onclick="callPostMethod()">POST /api/HelloWorld</button>
    <br />
    <span id="helloWorldPostResultPlaceHolder">ここにHelloWorldAPI(POST)の結果が入ります</span>
</div>

<script type="text/javascript">
    function callPostMethod() {
        var data = {};
        data.name = $("#inputText").val();
        // jQueryを使って呼ぶだけ
        $.ajax("/api/HelloWorld", { type: "POST", data: data }).then(function (r) {
            $("#helloWorldPostResultPlaceHolder").text(r);
        }, function (e) {
            alert("error: " + e);
        });
    }
</script>

ちなみに、戻り値もオブジェクトの形にしたい場合は以下のようにします。JavaScriptからアクセスしたときに不自然な名前にならないように、JsonProperty属性をつけて小文字はじまりの名前を指定してあげるとよさげ。

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;

namespace HelloWebApi.Controllers
{
    public class HelloWorldController : ApiController
    {
        // /api/HelloWorld
        public string Get()
        {
            return "Hello world";
        }

        // POST /api/HelloWorld
        public Data Post(Data data)
        {
            return new Data { Name = data.Name + " Hello world" };
        }
    }

    /// <summary>
    /// JSONのデータの入れ物
    /// </summary>
    public class Data
    {
        [JsonProperty(PropertyName = "name")]
        public string Name { get; set; }
    }
}

JavaScriptは以下のようになります。

function callPostMethod() {
    var data = {};
    data.name = $("#inputText").val();
    // jQueryを使って呼ぶだけ
    $.ajax("/api/HelloWorld", { type: "POST", data: data }).then(function (r) {
        $("#helloWorldPostResultPlaceHolder").text(r.name);
    }, function (e) {
        alert("error: " + e);
    });
}

まとめ

Postメソッドをstring Post(string name)みたいにしてJSから呼ぶ方法はどうやるんだろう。[FromBody]とかつけてみたりしたけどあかんかった。