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

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

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