MAUI加Blazor做一个跨平台的记账APP(三)请求后端接口
前面记录了加页面,但是需要从后端请求数据,来灵活地更新页面展示情况,记账毕竟也是要个人特有的,像账户和细目啊这些。于是以登录获取token为例记录一下请求后端接口。
在项目根目录新增Models和Services文件夹。
在Models目录下新增一个文件Token.cs,用以定义后端返回数据中需要处理的字段。
namespace accountingMAUIBlazor.Models;
public class Token
{
public string username { get; set; }
public string mail { get; set; }
public string token { get; set; }
}
在Services目录下先新增一个interface文件IUserService.cs,定义用户的相关行为,这里仅包含获取token。
namespace accountingMAUIBlazor.Services;
public interface IUserService
{
Task<String> GetTokenAsync();
}
同目录继续新增一个文件UserService.cs,作为对应接口文件的具体实现。这里先将登陆账户写死,等后续修改再调整为传递表单参数。定义是否登录和token值,如果已登录或者token不为空则直接返回,否则就向后端请求token再解析json数据后返回。这里用了异步请求避免卡顿,于是看到async和await,并且需要写作Task。
using System.Text.Json;
using System.Text;
using accountingMAUIBlazor.Models;
namespace accountingMAUIBlazor.Services;
public class UserService : IUserService
{
public bool IsLoggedIn { get; set; } = false;
private string _token = string.Empty;
public async Task<String> GetTokenAsync() //remember to add user parameter
{
if (!string.IsNullOrWhiteSpace(_token) || IsLoggedIn)
{
Console.WriteLine("no need to request api, return existent token");
return _token;
}
using var httpClient = new HttpClient();
HttpResponseMessage response;
try
{
var loginData = new {
username = "Bob",
password = "Bob"
};
var loginContent = new StringContent(JsonSerializer.Serialize(loginData), Encoding.UTF8, "application/json");
response =
await httpClient.PostAsync("http://127.0.0.1:8000/external/api/login/", loginContent);
response.EnsureSuccessStatusCode();
}
catch (Exception e)
{
Console.WriteLine("Error when request api: " + e.Message);
return _token;
}
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine("result: " + result);
try
{
_token = JsonSerializer.Deserialize<Token>(result)?.token ?? throw new JsonException();
IsLoggedIn = true;
}
catch (Exception e)
{
Console.WriteLine("Error when handle json: " + e.Message);
return _token;
}
return _token;
}
}
增加完了后需要注意在其他两个地方引用,否则页面调用会报错。
一是_Imports.razor加上
@using accountingMAUIBlazor.Services
二是MauiProgram.cs中加上引用,注意这里用了AddSingleton。这种有三类:
- AddTransient (Transient objects are always different; a new instance is provided to every controller and every service.)
- AddScoped (Scoped objects are the same within a request, but different across different requests.)
- AddSingleton (Singleton objects are the same for every object and every request.)
using Microsoft.Extensions.Logging;
using accountingMAUIBlazor.Data;
using accountingMAUIBlazor.Services; //here
namespace accountingMAUIBlazor;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<App>().ConfigureFonts(fonts =>{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
builder.Services.AddSingleton<IUserService, UserService>(); //here
builder.Services.AddMauiBlazorWebView();
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
builder.Logging.AddDebug();
#endif
builder.Services.AddSingleton<WeatherForecastService>();
return builder.Build();
}
}
接下来需要再增加一个简单的登陆页面来验证一下,在pages下新增Login.razor和Login.razor.cs。
Login.razor:
@inject NavigationManager NavigationManager
@inject IUserService _userService
<h3>Test Login</h3>
<p>Clicked Times: @click_count</p>
<p>token: @token</p>
<button class="btn btn-primary" @onclick="LoginWithoutInput">Click to login directly</button>
Login.razor.cs:
namespace accountingMAUIBlazor.Pages;
public partial class Login
{
private string token;
private int click_count = 0;
public async void LoginWithoutInput()
{
Console.WriteLine("start to login...");
click_count += 1;
token = await _userService.GetTokenAsync();
Console.WriteLine($"Token is: {token}");
NavigationManager.NavigateTo("/accounts");
}
}
运行起来后就能看到点击页面上的按钮,则会跳转到accounts页面;可以注释掉NavigateTo这一句,就能看到点击后点击次数会加一显示,token的值要是请求成功也会成功显示出来。当然这里的逻辑不严谨,等后面再改。