MAUI を始める、というか調べることにしたので、なにはともあれ Web API を呼び出して、なにかリスト表示をしようと作ってみる。だが、作ってみる途中でどうも動かないので、備忘録に記録しておく。
実は MAUI のドキュメント自体は極めてすくなくて、.NET Multi-Platform App UI documentation – .NET MAUI | Microsoft Docs 程度しかない。なんというか、いまのところ何もない状態に近いので、肝心の画面をどう作っていくのか?に関しては全くわからない。でも、まったくわからないながらも、もともと Xamarin.Forms からの移行になるのだから、Xamarin.Forms の XAML 形式をコピペすればいけるだろう。ということで、最初は Visual Studio 2019 を使って Xamarin.Forms で作ってみる。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamarinWebApi.MainPage">
<ScrollView>
<Grid RowSpacing="25">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="web api test"
Grid.Row="0"
HorizontalOptions="CenterAndExpand" />
<Button Text="call group"
Grid.Row="1"
HorizontalOptions="CenterAndExpand"
Clicked="clickGroup"/>
<ListView x:Name="lv" Grid.Row="2">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ScrollView>
</ContentPage>
たぶん、こんな風に ListView を使ったはず。ボタンをクリックしたときに Web API を呼び出すのはこんな感じ。
private async void clickGroup(object sender, EventArgs e)
{
var cl = new HttpClient();
var url = new Uri("http://192.168.1.28:5000/api/areagroup");
var json = await cl.GetStringAsync(url);
var js = new JsonSerializer();
var items = JsonConvert.DeserializeObject<List<AreaGroup>>(json);
this.lv.ItemsSource = items;
}
Web API 自体はローカルな dotnet で動かしているので、IP アドレスはローカルコンピュータのものになっている。Android エミュレータから呼び出すことになるので localhost ではなく、IP アドレスになっている。
さて、実はこれを動かすと次のようなエラーになる。

System.Net.WebException: 'Cleartext HTTP traffic to 192.168.1.28 not permitted'
とあるバージョンから Android は平文(HTTP)を通すことができないのである。
ああ、そうそう、ということで AndroidManifest.xml ファイルに android:usesCleartextTraffic=”true” の記述をいれることになる。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
<application
android:allowBackup="true"
android:icon="@mipmap/appicon"
android:roundIcon="@mipmap/appicon_round"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
>
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
これでデータが取得できるようになる。
デバッグ用の場合は平文の HTTP を通すのが手っ取り早いのだけど、一応 HTTPS のほうも通しておきたいと思って、書き換えてためしてみると。
var url = new Uri("http://192.168.1.28:5000/api/areagroup");

ということで、次のようにコードを書き換える。
private async void clickGroup(object sender, EventArgs e)
{
var httpHandler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (o, cert, chain, errors) => true };
var cl = new HttpClient(httpHandler);
var url = new Uri("https://192.168.1.28:5001/api/areagroup");
var json = await cl.GetStringAsync(url);
var js = new JsonSerializer();
var items = JsonConvert.DeserializeObject<List<AreaGroup>>(json);
this.lv.ItemsSource = items;
}
HttpClient のインスタンスを作成するときに、証明書のエラーをスルーするようにオプションを入れる。これも以前みたようなことがあった(また結構さがしたけど)。
これで無事通るようになる。
さて、Xamarin.Forms で動くようになったので、これを Visual Studio 2022 の MAUI のほうにコピーする。
XAML のほうは、そのままコピーで ok.
コードのほうはこんな感じ。
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.Maui.Controls;
using System.Net.Http;
using System.Net.Http.Json;
using System.Collections.Generic;
namespace HelloWebApi
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void clickGroup(object sender, EventArgs e)
{
var httpHandler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (o, cert, chain, errors) => true };
var cl = new HttpClient(httpHandler);
var url = new Uri("https://192.168.1.28:5001/api/areagroup");
var items = await cl.GetFromJsonAsync<List<AreaGroup>>(url);
this.lv.ItemsSource = items;
}
}
JSON のパースは GetFromJsonAsync が使えるようになったので楽ですね。とか思ったら、ServerCertificateCustomValidationCallback なところでエラーが発生します。

どうやら、.NET 6(かな?)のほうでは現状 ServerCertificateCustomValidationCallback がサポートされていない模様。どうも次の preview のバージョンでこれは入る模様です。
仕方がないので、再び AndroidManifest.xml に android:usesCleartextTraffic=”true” を追加して HTTP で通すことにする。

これを Android エミュレータで動かすと。

ちなみに、Windowsアプリのほう(実質 UWP)は動いてます。

懐かしの UWP な感じがするのだけど、これ、レイアウトが MainPage.xaml で完全に共有になっているので、マージンの調節とかが大変そう。Android と iOS 間で十分面倒なのだけど。
ひとまず、ここまで動いたという記録。