ASP.NET Core MVC の Web API で JSON 形式のデータを扱う

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 クラスを作っておく。

[code lang=”csharp”]
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
[/code]

JsonSerializer は、IEnumerable<Person> もでシリアライズしてくれるので、XML 形式のような People クラスは不要で、そのまま使える。

コントローラーを作るときに「Entitiy Frameworkを使用したアクションがあるAPIコントローラー」を選べば、CURD機能のAPIが、ずらっと出力される。

このまま動くので改変しなくてよい。

[code lang=”csharp”]
[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 GetPerson()
{
return _context.Person;
}

// GET: api/People/5
[HttpGet(“{id}”)]
public async Task 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 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 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 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);
}
///

/// 受け口を POST に変換する
///

[HttpPost(“{id}”)]
[Route(“Edit/{id}”)]
public async Task Edit([FromRoute] int id, [FromBody] Person person)
{
return await PutPerson(id, person);
}
[HttpPost]
[Route(“Create”)]
public async Task Create([FromBody] Person person)
{
return await PostPerson(person);
}
}
[/code]

クライアントからの表示の都合上 PutPerson の戻り値を変えている。
あと、POST 形式だけで通るように、Edit と Create を追加している。

WPF クライアントを作る

XML 形式のときと同じように、Person クラスだけを作る。
JsonSerializer が使えるように、Newtonsoft.Json を NuGet で参照設定させておく。

[code lang=”csharp”]
namespace ClientJson
{
///

/// MainWindow.xaml の相互作用ロジック
///

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>(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(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(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(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(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(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; }
}
}
[/code]

  • 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

カテゴリー: ASP.NET パーマリンク