ASP.net Core 2.0で、swaggerつかって、クライアント側で、REST APIを使って、Client Proxyを作って、Windows Forms でいろいろする。
いや、まぁ、WPFでもいいですし、UWPでもいいんですけどね。あと、今回ご紹介する方法は、swagger.json のURLをAPI毎に任意にわけるとか、そんな感じの事もします。けども、swaggerの使い方とかは触れないので、swaggerの使い方を知りたい人は回れ右で、他のもっと親切なサイト様へどうぞ。
でで。
やりたいことしては、REST APIを使って、Client Proxyしたいので、まずは、ASP.net Core 2.0 で、Web APIサイトを作りますですます。あ、Visual Studio 2017です。15.4.2です。当然、.net Coreあたりの開発はインストールしておいてくださいね。Azure系のも必要です。(REST API Proxyを作るのに使うんだっけな)まぁ、最新にしておけって事ですよ。あ、C#です。
さて、プロジェクト作りまーす。説明しません。新規プロジェクト-C#-Web-ASP.NET Core Webアプリケーションで、Web APIで作ります。Core 2.0 にするんだよ。名前は何でもいいよ。WebApiSampleにしたけど。
画像、用意してたけど、張ってたらなんか、タブレットだとうまくいかない。まぁ、文字でいいだろ、文字で。こんな記事。
で、なんだっけ。あ、新規作成したんだ。で、作ったら、ソリューションを右クリックして、NeGetパッケージの管理をクリック。NuGetでたら、参照で、Swashbuckle.AspNetCoreといれて、検索。AspNetCoreだよ。間違えるなー。
で、Swashbuckleを使えるようにするんだけど、XMLドキュメントはけるようにしておく。あとでXMLドキュメントの設定もする。プロジェクトのプロパティから、ビルドにいって、出力の、XMLドキュメントにチェックね。
で、Startup.cs開いて、Swagger使えるようにする。あ、ValuesControllerあるよね? ちゃんと。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Swashbuckle.AspNetCore.Swagger;
using System.IO;
using Microsoft.Extensions.PlatformAbstractions;
namespace WebApiSample
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//Swaggerを登録
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Info
{
Title = "WebTestAPI",
Version = "v1"
});
// Set the comments path for the Swagger JSON and UI.
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
var xmlPath = Path.Combine(basePath, "WebApiSample.xml"); //ここ、出力されるxmlコメントのファイル名ね
options.IncludeXmlComments(xmlPath);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc();
// Swagger UI を使う
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "WebTestAPI");
});
}
}
}
まぁ、何が足してあって何が足してないかって、並べて見てみて。でで、これで実行すれば、とりあえずswagger uiが見られるんだけど、あとあと面倒なので、プロジェクトのプロパティのデバッグの、ブラウザーの起動のところ、swagger/にしておくと後で楽。
起動した? Swagger UI見られた? OK、次に行こう。見られなかった人は、別の、もっと親切なサイトで設定しよう。
クライアントを作ります。クライアントは、.NET Framework 4.6.2 にします。というか、ここはCoreじゃないです。Coreはできないんじゃないかな。とりあえず、SampleClientとかで適当に作ります。
作ったら、プロジェクトを右クリック。追加-REST APIクライアントをクリックします。出てない時は、Azure開発が入っていないかも。
出てくる画面に、Swagger URLを入れます。ここに入れるURLは、先のSwagger UIに出てきたURLです。画面の中にあるやつね。ファイル名が、swagger.jsonのやつ。swagger.josnまで、全部いれます。で、クライアント名前空間は、ルートの名前空間になっちゃうんですけど、クライアントプロキシ名にもされるので、入れます。WebTestAPIとしました。
追加すると、なんかわしゃわしゃ、新しい物が追加されます。うまくいかなかったら、Webサイトが起動しているか、確認。IIS Expressだと思いますんで、動いているか確認。動かし方がわからんて? wwwrootを右クリックして、ブラウザーで表示ってやれば動くよ。たぶん。
でで、なんかNuGetがRest Clientとか、Json.netとか足すんですが、バージョンが古いと思うので、NeGetで最新にしてください。最新でないと、いろいろめんどくさい。
今回は認証機能ないので、認証しないでつなげられるように、クラスを1つ追加。普通に追加。クラス名はAnonymousCredentialで、コードは以下。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Rest;
namespace SampleClient
{
public class AnonymousCredential : ServiceClientCredentials
{
}
}
Form1があるだろうから、ボタンコントロールおいて、ボタンクリックイベントに以下。
private void button1_Click(object sender, EventArgs e)
{
var client = new WebTestAPI.WebTestAPIClient(
new Uri("http://localhost:13418/"), //アドレスは変えろよ
new AnonymousCredential()
);
//usingしてないとだめよ using WebTestAPI;
var result = client.ApiValuesGet();
foreach (var x in result)
{
MessageBox.Show(x);
}
var r = client.ApiValuesByIdGet(12);
MessageBox.Show(r);
}
コンパイルして実行しましょう。通らねーの人は、コメントにある、using WebTestAPIしたけ? まあ、つけたプロキシ名がこれじゃなかったら、その名前になるんだけど。
これ、実態は WebTestAPIClient で、これが動作するんだけど、インターフェイスプログラミングに則って、操作のインターフェイスは、IWebTestAPIClient になってるわけ。で、このIWebTestAPIClientに対して、WebTestAPIClientExtensions から提供される拡張メソッドが付く。で、ここに同期呼び出しの拡張がついている。
こういうテクニックは、これはこれで使えるので、知っておくと便利。
それでも通らない時は、NuGetで最新とっているか確認して、とってきて、Proxyクラスの入っているフォルダ(今だと、WebTestAPICliet)を削除して、REST API を再作成だ。
それでも駄目なら、あきらめろん。(右上の小窓のアヒルに聞いたら教えてくれる可能性も微レ存)
で、実行する。と、うまくいくー! かに見えるが、ID指定の方で、おちるー! 落ちない人は、よかったなー! 修正されているか、または、解決策2を見ろ。
でで。これなんだけど、これは、ASP.net Core 2 の問題か、REST APIの問題か、Json.NETの問題かわからないが、Bodyで帰ってくる文字列が、text/Plainなので、value になっているため、"value" でないので、Format Exception的なものが発生する問題なのね。なので、解決策は以下。
解決策1
実際に使いそうなパターンに変更する。
クラスにラップするだけで十分。
解決策2
もう、そもそもjsonしか返さないようにしちゃう。
クラスに [Produces("application/json")] を付ける方法。
Content Negotiation 完全無視!
でも、新規の空のAPIコントローラーを作成すると、勝手につくし! どうせ付くなら、別にいっか! 的な!
お好みで。ここでは、別にどうでもいいので、クラスでラップします。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace WebApiSample.Controllers
{
public class Values
{
public string Value { get; set; }
}
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable
{
return new Values[]
{
new Values { Value= "value1" },
new Values { Value= "value2" },
};
}
// GET api/values/5
[HttpGet("{id}")]
public Values Get(int id)
{
return new Values { Value = $"value{id}" };
}
// POST api/values
[HttpPost]
public void Post([FromBody]string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}
クリックの中はこんな感じになるかな。
private void button1_Click(object sender, EventArgs e)
{
var client = new WebTestAPI.WebTestAPIClient(
new Uri("http://localhost:13418/"), //アドレスは変えろよ
new AnonymousCredential()
);
//usingしてないとだめよ using WebTestAPI;
var result = client.ApiValuesGet();
foreach (var x in result)
{
MessageBox.Show(x.Value);
}
var r = client.ApiValuesByIdGet(12);
MessageBox.Show(r.Value);
}
はい、次。やっと本題。Web APIドキュメントを、コントローラー毎に生成する方法。コントローラー毎にWeb APIドキュメントができれば、Proxyもコントローラー毎に作れて、管理しやすい。
とまれ、新規のコントローラーの追加。DefaultControllerでいいや。面倒くさいし。読み取りと書き込みありのパターンでいいや、めんどくさいし。
いったん、これでサイトを実行してみましょう。ちゃんとswagger UIで、Defaultと、先に作っていたValuesがあるますか? あるます前提で続けます。
両方でるので、これのURLを分割したいのです。でで、どういう仕組みでswaggerが、生成されるswagger.jsonのアドレスを管理しているのかというと、これは、Startup.csの、UseSwaggerUIの中の、SwaggerEndpointで管理されているのですよ。ここのアドレス、/swagger/v1/swagger.jsonのとこ。
でね、この、v1が肝なんですよ。このv1のところがね、SwaggerDocの、nameなんですよ。つまりね、拡張すると、以下のようになるの。
//Swaggerを登録
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Info
{
Title = "WebTestAPI",
Version = "v1"
});
options.SwaggerDoc("DefaultControler", new Info
{
Title = "Default API",
Version = "v1"
});
// Set the comments path for the Swagger JSON and UI.
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
var xmlPath = Path.Combine(basePath, "WebApiSample.xml"); //ここ、出力されるxmlコメントのファイル名ね
options.IncludeXmlComments(xmlPath);
});
// Swagger UI を使う
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "WebTestAPI");
options.SwaggerEndpoint("/swagger/DefaultControler/swagger.json", "Default API");
});
で、実行してみましょ。あれ? 何も変わらない? いえ、右上のドロップダウンリストのところ、選べません? 選ぶと、左のテキストボックスのアドレス、変わりません?
で、ここまで来たら、後はコントローラーがどっちに属するか、設定できればいいんで、それは、ApiExplorerSettings Attributeです。以下みたいな感じ。
[Produces("application/json")]
[Route("api/Default")]
[ApiExplorerSettings(GroupName = "DefaultControler")]
public class DefaultController : Controller
{
[Route("api/[controller]")]
[ApiExplorerSettings(GroupName = "v1")]
public class ValuesController : Controller
{
はいじっこー。分割できた? できたね? できたら、もう簡単だね。アドレスが違うなら、REST APIクライアントは複数作れるね。はい、できたー!
備忘録的なメモでもあるんだけど、なげーわ、なげーよ、アヒル。
コメントする