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

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

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