Files
wpywmail/client-winui/ApiClient.cs
T

98 lines
4.5 KiB
C#

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
namespace WpywMail.Client;
public sealed class ApiClient
{
private readonly HttpClient http = new() { Timeout = TimeSpan.FromSeconds(12) };
private readonly JsonSerializerOptions json = new(JsonSerializerDefaults.Web);
private string token = "";
public string BaseUrl { get; private set; }
public ApiClient(string baseUrl = "http://127.0.0.1:8787/api")
{
BaseUrl = NormalizeBaseUrl(baseUrl);
}
public void SetBaseUrl(string baseUrl) => BaseUrl = NormalizeBaseUrl(baseUrl);
public async Task<LoginResponse> LoginAsync(string email, string password, CancellationToken cancellationToken = default)
{
using var response = await http.PostAsJsonAsync($"{BaseUrl}/login", new { email, password }, json, cancellationToken);
return await ReadOrThrow<LoginResponse>(response, cancellationToken);
}
public async Task<MeResponse> GetMeAsync(CancellationToken cancellationToken = default) =>
await SendAsync<MeResponse>(HttpMethod.Get, "/me", cancellationToken: cancellationToken);
public async Task<ConfigResponse> GetConfigAsync(CancellationToken cancellationToken = default) =>
await SendAsync<ConfigResponse>(HttpMethod.Get, "/config", cancellationToken: cancellationToken);
public async Task<IReadOnlyList<MailSummary>> GetMessagesAsync(string folder, string query = "", CancellationToken cancellationToken = default)
{
var url = $"/messages?folder={Uri.EscapeDataString(folder)}";
if (!string.IsNullOrWhiteSpace(query)) url += $"&q={Uri.EscapeDataString(query)}";
var result = await SendAsync<MessageListResponse>(HttpMethod.Get, url, cancellationToken: cancellationToken);
return result.Messages;
}
public async Task<MailMessage> GetMessageAsync(string id, CancellationToken cancellationToken = default)
{
var result = await SendAsync<MessageDetailResponse>(HttpMethod.Get, $"/messages/{Uri.EscapeDataString(id)}", cancellationToken: cancellationToken);
return result.Message;
}
public async Task SendMessageAsync(string to, string subject, string text, CancellationToken cancellationToken = default) =>
await SendAsync<object>(HttpMethod.Post, "/send", new { to, subject, text }, cancellationToken);
public async Task ChangePasswordAsync(string password, CancellationToken cancellationToken = default) =>
await SendAsync<object>(HttpMethod.Post, "/account/password", new { password }, cancellationToken);
public async Task LogoutAsync(CancellationToken cancellationToken = default)
{
try { await SendAsync<object>(HttpMethod.Post, "/logout", cancellationToken: cancellationToken); }
finally { token = ""; }
}
public void SetToken(string value)
{
token = value;
http.DefaultRequestHeaders.Authorization = string.IsNullOrWhiteSpace(token) ? null : new AuthenticationHeaderValue("Bearer", token);
}
private async Task<T> SendAsync<T>(HttpMethod method, string path, object? body = null, CancellationToken cancellationToken = default)
{
using var request = new HttpRequestMessage(method, $"{BaseUrl}{path}");
if (body is not null) request.Content = JsonContent.Create(body, options: json);
using var response = await http.SendAsync(request, cancellationToken);
return await ReadOrThrow<T>(response, cancellationToken);
}
private async Task<T> ReadOrThrow<T>(HttpResponseMessage response, CancellationToken cancellationToken)
{
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync<T>(json, cancellationToken);
return result ?? throw new InvalidOperationException("服务端返回了空响应");
}
var message = await response.Content.ReadAsStringAsync(cancellationToken);
try
{
var error = JsonSerializer.Deserialize<Dictionary<string, string>>(message, json);
if (error?.TryGetValue("error", out var detail) == true) throw new InvalidOperationException(detail);
}
catch (JsonException) { }
throw new InvalidOperationException($"服务端返回 HTTP {(int)response.StatusCode}");
}
private static string NormalizeBaseUrl(string value)
{
var url = string.IsNullOrWhiteSpace(value) ? "http://127.0.0.1:8787/api" : value.Trim().TrimEnd('/');
return url.EndsWith("/api", StringComparison.OrdinalIgnoreCase) ? url : $"{url}/api";
}
}