游标分页初步

This commit is contained in:
biss committed 2026-08-23 09:11:48 +08:00
1 parent 14af0910e7
commit bdb2e54521
5 files changed
+99 -20

No files matched your search

+29
View File
@@ -0,0 +1,29 @@
using System.Text;
using System.Text.Json;
namespace Jiaowu.Api.Contracts;
/// <summary>Encodes endpoint-specific keyset values without exposing a query offset.</summary>
public static class CursorToken
{
private static readonly JsonSerializerOptions Options = new(JsonSerializerDefaults.Web);
public static string Encode<T>(T value) =>
Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(value, Options)))
.TrimEnd('=').Replace('+', '-').Replace('/', '_');
public static bool TryDecode<T>(string? token, out T? value)
{
value = default;
if (string.IsNullOrWhiteSpace(token)) return true;
try
{
var normalized = token.Replace('-', '+').Replace('_', '/');
normalized = normalized.PadRight(normalized.Length + (4 - normalized.Length % 4) % 4, '=');
value = JsonSerializer.Deserialize<T>(Encoding.UTF8.GetString(Convert.FromBase64String(normalized)), Options);
return value is not null;
}
catch (JsonException) { return false; }
catch (FormatException) { return false; }
}
}
+14
View File
@@ -1,5 +1,19 @@
namespace Jiaowu.Api.Contracts;
/// <summary>
/// A forward-only keyset page. <see cref="NextCursor"/> is opaque to clients and
/// must be sent back unchanged as the <c>cursor</c> query parameter.
/// </summary>
public sealed record CursorPagedResult<T>(
IReadOnlyCollection<T> Items,
string? NextCursor,
bool HasMore,
int PageSize);
/// <summary>
/// Legacy offset-page response. New list endpoints must use
/// <see cref="CursorPagedResult{T}"/> instead.
/// </summary>
public sealed record PagedResult<T>(
IReadOnlyCollection<T> Items,
int Total,