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 クラスを作っておく。
1 2 3 4 5 6 | 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が、ずらっと出力される。
このまま動くので改変しなくてよい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | [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 で参照設定させておく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | 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