Web API の JSON 形式に関しては、
Building Your First Web API with ASP.NET Core MVC and Visual Studio ? ASP.NET documentation
https://docs.asp.net/en/latest/tutorials/first-web-api.html
に詳しい解説があります。
が、クライアント側が書いていないので、先の XML 形式と同じように WFP アプリでクライアントを書いていきます。
送受信の形式
- WPF アプリで JSON 形式で送信
- ASP.NET Core Web API で JSON 形式で返信
することを考える。データはいちいち JSON 形式に直すのは面倒なので、C# のクラスから Newtonsoft.Json.JsonSerializer を使ってシリアライズ/デシリアライズをする。
Web API 側の設定
XML 形式の場合は Formatters を追加したが、JSON の場合はもともとロードされているので不要。
JsonOutputFormatter と JsonInputFormatter が初期値で使われている。
Web API の PeopleController クラスを作る
Modelクラスである Person クラスを作っておく。
public class Person { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } }
JsonSerializer は、IEnumerable<Person> もでシリアライズしてくれるので、XML 形式のような People クラスは不要で、そのまま使える。
コントローラーを作るときに「Entitiy Frameworkを使用したアクションがあるAPIコントローラー」を選べば、CURD機能のAPIが、ずらっと出力される。
このまま動くので改変しなくてよい。
[Produces("application/json")] [Route("api/People")] public class PeopleController : Controller { private readonly ApplicationDbContext _context; public PeopleController(ApplicationDbContext context) { _context = context; } // GET: api/People [HttpGet] public IEnumerable<Person> GetPerson() { return _context.Person; } // GET: api/People/5 [HttpGet("{id}")] public async Task<IActionResult> GetPerson([FromRoute] int id) { if (!ModelState.IsValid) { return BadRequest(ModelState); } Person person = await _context.Person.SingleOrDefaultAsync(m => m.Id == id); if (person == null) { return NotFound(); } return Ok(person); } // PUT: api/People/5 [HttpPut("{id}")] public async Task<IActionResult> PutPerson([FromRoute] int id, [FromBody] Person person) { if (!ModelState.IsValid) { return BadRequest(ModelState); } if (id != person.Id) { return BadRequest(); } _context.Entry(person).State = EntityState.Modified; try { await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!PersonExists(id)) { return NotFound(); } else { throw; } } // return NoContent(); return await GetPerson(person.Id); } // POST: api/People [HttpPost] public async Task<IActionResult> PostPerson([FromBody] Person person) { if (!ModelState.IsValid) { return BadRequest(ModelState); } _context.Person.Add(person); try { await _context.SaveChangesAsync(); } catch (DbUpdateException) { if (PersonExists(person.Id)) { return new StatusCodeResult(StatusCodes.Status409Conflict); } else { throw; } } return CreatedAtAction("GetPerson", new { id = person.Id }, person); } // DELETE: api/People/5 [HttpDelete("{id}")] public async Task<IActionResult> DeletePerson([FromRoute] int id) { if (!ModelState.IsValid) { return BadRequest(ModelState); } Person person = await _context.Person.SingleOrDefaultAsync(m => m.Id == id); if (person == null) { return NotFound(); } _context.Person.Remove(person); await _context.SaveChangesAsync(); return Ok(person); } private bool PersonExists(int id) { return _context.Person.Any(e => e.Id == id); } /// <summary> /// 受け口を POST に変換する /// </summary> [HttpPost("{id}")] [Route("Edit/{id}")] public async Task<IActionResult> Edit([FromRoute] int id, [FromBody] Person person) { return await PutPerson(id, person); } [HttpPost] [Route("Create")] public async Task<IActionResult> Create([FromBody] Person person) { return await PostPerson(person); } }
クライアントからの表示の都合上 PutPerson の戻り値を変えている。
あと、POST 形式だけで通るように、Edit と Create を追加している。
WPF クライアントを作る
XML 形式のときと同じように、Person クラスだけを作る。
JsonSerializer が使えるように、Newtonsoft.Json を NuGet で参照設定させておく。
namespace ClientJson { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private async void clickGet(object sender, RoutedEventArgs e) { var hc = new HttpClient(); // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var res = await hc.GetAsync("http://localhost:5000/api/people"); var str = await res.Content.ReadAsStringAsync(); textXml.Text = str; var js = new Newtonsoft.Json.JsonSerializer(); var jr = new Newtonsoft.Json.JsonTextReader( new System.IO.StringReader(str)); var items = js.Deserialize<IEnumerable<Person>>(jr); textPerson.Text = ""; foreach (var item in items) { textPerson.Text += $"{item.Id} {item.Name} {item.Age} n"; } } private async void clickGetById(object sender, RoutedEventArgs e) { int id = 2; var hc = new HttpClient(); // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var res = await hc.GetAsync($"http://localhost:5000/api/people/{id}"); var str = await res.Content.ReadAsStringAsync(); textXml.Text = str; var js = new Newtonsoft.Json.JsonSerializer(); var jr = new Newtonsoft.Json.JsonTextReader(new System.IO.StringReader(str)); var item = js.Deserialize<Person>(jr); textPerson.Text = $"{item.Id} {item.Name} {item.Age}"; } private async void clickPutById(object sender, RoutedEventArgs e) { var person = new Person() { Id = 2, Name = "update person", Age = 99 }; var js = new Newtonsoft.Json.JsonSerializer(); var sw = new System.IO.StringWriter(); js.Serialize(sw, person); var hc = new HttpClient(); // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var json = sw.ToString(); var cont = new StringContent(json, Encoding.UTF8, "application/json"); var res = await hc.PutAsync($"http://localhost:5000/api/people/{person.Id}", cont); var str = await res.Content.ReadAsStringAsync(); textXml.Text = str; var jr = new Newtonsoft.Json.JsonTextReader(new System.IO.StringReader(str)); var item = js.Deserialize<Person>(jr); textPerson.Text = $"{item.Id} {item.Name} {item.Age}"; } private async void clickPost(object sender, RoutedEventArgs e) { var person = new Person() { Id = 0, Name = "new person", Age = 88 }; var js = new Newtonsoft.Json.JsonSerializer(); var sw = new System.IO.StringWriter(); js.Serialize(sw, person); var hc = new HttpClient(); // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var json = sw.ToString(); var cont = new StringContent(json, Encoding.UTF8, "application/json"); var res = await hc.PostAsync($"http://localhost:5000/api/people", cont); var str = await res.Content.ReadAsStringAsync(); textXml.Text = str; var jr = new Newtonsoft.Json.JsonTextReader(new System.IO.StringReader(str)); var item = js.Deserialize<Person>(jr); textPerson.Text = $"{item.Id} {item.Name} {item.Age}"; } private void clickDeleteById(object sender, RoutedEventArgs e) { } private async void clickCreate(object sender, RoutedEventArgs e) { var person = new Person() { Id = 0, Name = "new person", Age = 88 }; var js = new Newtonsoft.Json.JsonSerializer(); var sw = new System.IO.StringWriter(); js.Serialize(sw, person); var hc = new HttpClient(); // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var json = sw.ToString(); var cont = new StringContent(json, Encoding.UTF8, "application/json"); var res = await hc.PostAsync($"http://localhost:5000/api/people/Create", cont); var str = await res.Content.ReadAsStringAsync(); textXml.Text = str; var jr = new Newtonsoft.Json.JsonTextReader(new System.IO.StringReader(str)); var item = js.Deserialize<Person>(jr); textPerson.Text = $"{item.Id} {item.Name} {item.Age}"; } private async void clickEdit(object sender, RoutedEventArgs e) { var person = new Person() { Id = 2, Name = "edit person", Age = 99 }; var js = new Newtonsoft.Json.JsonSerializer(); var sw = new System.IO.StringWriter(); js.Serialize(sw, person); var hc = new HttpClient(); // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var json = sw.ToString(); var cont = new StringContent(json, Encoding.UTF8, "application/json"); var res = await hc.PostAsync($"http://localhost:5000/api/people/Edit/{person.Id}", cont); var str = await res.Content.ReadAsStringAsync(); textXml.Text = str; var jr = new Newtonsoft.Json.JsonTextReader(new System.IO.StringReader(str)); var item = js.Deserialize<Person>(jr); textPerson.Text = $"{item.Id} {item.Name} {item.Age}"; } } } namespace SampleWebApiXml.Models { public class Person { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } }
- Web APIの戻り値形式が、デフォルトでJSONなので、 hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”)); は、設定しなくてもよい。
- Person クラスと JSON 形式は、JsonSerializer.Serialize と Deserializeを使えばよい。
- これも、日本語を通すためにはきちんと UTF8 エンコードが必要かも。
でもって、うまく動くと dotnet run でサーバーを起動、WPF クライアントから JSON 形式で送受信ができるようになる。ここまで、できるようになれば、クライアントを Javascript や Ruby に切り替えたり、サーバー側を CakePHP で切り替えて相互に動かせるようになる。
サンプルコード
動作できるサンプルコードはこちら
https://1drv.ms/u/s!AmXmBbuizQkXgfsRYiYpmZzonGIBZw