appwrite-dotnet
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAppwrite .NET SDK
Appwrite .NET SDK
Installation
安装
bash
dotnet add package Appwritebash
dotnet add package AppwriteSetting Up the Client
客户端设置
csharp
using Appwrite;
using Appwrite.Services;
using Appwrite.Models;
var client = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject(Environment.GetEnvironmentVariable("APPWRITE_PROJECT_ID"))
.SetKey(Environment.GetEnvironmentVariable("APPWRITE_API_KEY"));csharp
using Appwrite;
using Appwrite.Services;
using Appwrite.Models;
var client = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject(Environment.GetEnvironmentVariable("APPWRITE_PROJECT_ID"))
.SetKey(Environment.GetEnvironmentVariable("APPWRITE_API_KEY"));Code Examples
代码示例
User Management
用户管理
csharp
var users = new Users(client);
// Create user
var user = await users.Create(ID.Unique(), "user@example.com", null, "password123", "User Name");
// List users
var list = await users.List(new List<string> { Query.Limit(25) });
// Get user
var fetched = await users.Get("[USER_ID]");
// Delete user
await users.Delete("[USER_ID]");csharp
var users = new Users(client);
// 创建用户
var user = await users.Create(ID.Unique(), "user@example.com", null, "password123", "User Name");
// 列出用户
var list = await users.List(new List<string> { Query.Limit(25) });
// 获取用户
var fetched = await users.Get("[USER_ID]");
// 删除用户
await users.Delete("[USER_ID]");Database Operations
数据库操作
Note: Use(not the deprecatedTablesDBclass) for all new code. Only useDatabasesif the existing codebase already relies on it or the user explicitly requests it.DatabasesTip: Prefer named arguments (e.g.,) for all SDK method calls. Only use positional arguments if the existing codebase already uses them or the user explicitly requests it.databaseId: "..."
csharp
var tablesDB = new TablesDB(client);
// Create database
var db = await tablesDB.Create(ID.Unique(), "My Database");
// Create row
var doc = await tablesDB.CreateRow("[DATABASE_ID]", "[TABLE_ID]", ID.Unique(),
new Dictionary<string, object> { { "title", "Hello World" } });
// Query rows
var results = await tablesDB.ListRows("[DATABASE_ID]", "[TABLE_ID]",
new List<string> { Query.Equal("title", "Hello World"), Query.Limit(10) });
// Get row
var row = await tablesDB.GetRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]");
// Update row
await tablesDB.UpdateRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]",
new Dictionary<string, object> { { "title", "Updated" } });
// Delete row
await tablesDB.DeleteRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]");注意: 所有新代码请使用(而非已弃用的TablesDB类)。仅当现有代码库已依赖Databases或用户明确要求时,才使用该类。Databases提示: 所有SDK方法调用优先使用命名参数(例如)。仅当现有代码库已使用位置参数或用户明确要求时,才使用位置参数。databaseId: "..."
csharp
var tablesDB = new TablesDB(client);
// 创建数据库
var db = await tablesDB.Create(ID.Unique(), "My Database");
// 创建行
var doc = await tablesDB.CreateRow("[DATABASE_ID]", "[TABLE_ID]", ID.Unique(),
new Dictionary<string, object> { { "title", "Hello World" } });
// 查询行
var results = await tablesDB.ListRows("[DATABASE_ID]", "[TABLE_ID]",
new List<string> { Query.Equal("title", "Hello World"), Query.Limit(10) });
// 获取行
var row = await tablesDB.GetRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]");
// 更新行
await tablesDB.UpdateRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]",
new Dictionary<string, object> { { "title", "Updated" } });
// 删除行
await tablesDB.DeleteRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]");String Column Types
字符串列类型
Note: The legacytype is deprecated. Use explicit column types for all new columns.string
| Type | Max characters | Indexing | Storage |
|---|---|---|---|
| 16,383 | Full index (if size ≤ 768) | Inline in row |
| 16,383 | Prefix only | Off-page |
| 4,194,303 | Prefix only | Off-page |
| 1,073,741,823 | Prefix only | Off-page |
- is stored inline and counts towards the 64 KB row size limit. Prefer for short, indexed fields like names, slugs, or identifiers.
varchar - ,
text, andmediumtextare stored off-page (only a 20-byte pointer lives in the row), so they don't consume the row size budget.longtextis not required for these types.size
csharp
// Create table with explicit string column types
await tablesDB.CreateTable("[DATABASE_ID]", ID.Unique(), "articles",
new List<object> {
new { key = "title", type = "varchar", size = 255, required = true }, // inline, fully indexable
new { key = "summary", type = "text", required = false }, // off-page, prefix index only
new { key = "body", type = "mediumtext", required = false }, // up to ~4 M chars
new { key = "raw_data", type = "longtext", required = false }, // up to ~1 B chars
});注意: 旧版类型已弃用。所有新列请使用明确的列类型。string
| 类型 | 最大字符数 | 索引方式 | 存储位置 |
|---|---|---|---|
| 16,383 | 全索引(若长度≤768) | 行内存储 |
| 16,383 | 仅前缀索引 | 页外存储 |
| 4,194,303 | 仅前缀索引 | 页外存储 |
| 1,073,741,823 | 仅前缀索引 | 页外存储 |
- 存储在行内,占用64 KB行大小限制。适合短的、需要索引的字段,如名称、别名或标识符。
varchar - 、
text和mediumtext存储在页外(行中仅保留20字节的指针),因此不会占用行大小配额。这些类型不需要指定longtext。size
csharp
// 创建包含明确字符串列类型的表
await tablesDB.CreateTable("[DATABASE_ID]", ID.Unique(), "articles",
new List<object> {
new { key = "title", type = "varchar", size = 255, required = true }, // 行内存储,可全索引
new { key = "summary", type = "text", required = false }, // 页外存储,仅前缀索引
new { key = "body", type = "mediumtext", required = false }, // 最多约400万字符
new { key = "raw_data", type = "longtext", required = false }, // 最多约10亿字符
});Query Methods
查询方法
csharp
// Filtering
Query.Equal("field", "value") // == (or pass array for IN)
Query.NotEqual("field", "value") // !=
Query.LessThan("field", 100) // <
Query.LessThanEqual("field", 100) // <=
Query.GreaterThan("field", 100) // >
Query.GreaterThanEqual("field", 100) // >=
Query.Between("field", 1, 100) // 1 <= field <= 100
Query.IsNull("field") // is null
Query.IsNotNull("field") // is not null
Query.StartsWith("field", "prefix") // starts with
Query.EndsWith("field", "suffix") // ends with
Query.Contains("field", "sub") // contains
Query.Search("field", "keywords") // full-text search (requires index)
// Sorting
Query.OrderAsc("field")
Query.OrderDesc("field")
// Pagination
Query.Limit(25) // max rows (default 25, max 100)
Query.Offset(0) // skip N rows
Query.CursorAfter("[ROW_ID]") // cursor pagination (preferred)
Query.CursorBefore("[ROW_ID]")
// Selection & Logic
Query.Select(new List<string> { "field1", "field2" })
Query.Or(new List<string> { Query.Equal("a", 1), Query.Equal("b", 2) }) // OR
Query.And(new List<string> { Query.GreaterThan("age", 18), Query.LessThan("age", 65) }) // AND (default)csharp
// 过滤
Query.Equal("field", "value") // ==(传入数组则为IN查询)
Query.NotEqual("field", "value") // !=
Query.LessThan("field", 100) // <
Query.LessThanEqual("field", 100) // <=
Query.GreaterThan("field", 100) // >
Query.GreaterThanEqual("field", 100) // >=
Query.Between("field", 1, 100) // 1 <= 字段 <= 100
Query.IsNull("field") // 为空
Query.IsNotNull("field") // 不为空
Query.StartsWith("field", "prefix") // 以指定前缀开头
Query.EndsWith("field", "suffix") // 以指定后缀结尾
Query.Contains("field", "sub") // 包含指定子串
Query.Search("field", "keywords") // 全文搜索(需要索引)
// 排序
Query.OrderAsc("field")
Query.OrderDesc("field")
// 分页
Query.Limit(25) // 最大行数(默认25,最大100)
Query.Offset(0) // 跳过N行
Query.CursorAfter("[ROW_ID]") // 游标分页(推荐使用)
Query.CursorBefore("[ROW_ID]")
// 字段选择与逻辑
Query.Select(new List<string> { "field1", "field2" })
Query.Or(new List<string> { Query.Equal("a", 1), Query.Equal("b", 2) }) // 或
Query.And(new List<string> { Query.GreaterThan("age", 18), Query.LessThan("age", 65) }) // 与(默认逻辑)File Storage
文件存储
csharp
var storage = new Storage(client);
// Upload file
var file = await storage.CreateFile("[BUCKET_ID]", ID.Unique(), InputFile.FromPath("/path/to/file.png"));
// List files
var files = await storage.ListFiles("[BUCKET_ID]");
// Delete file
await storage.DeleteFile("[BUCKET_ID]", "[FILE_ID]");csharp
var storage = new Storage(client);
// 上传文件
var file = await storage.CreateFile("[BUCKET_ID]", ID.Unique(), InputFile.FromPath("/path/to/file.png"));
// 列出文件
var files = await storage.ListFiles("[BUCKET_ID]");
// 删除文件
await storage.DeleteFile("[BUCKET_ID]", "[FILE_ID]");InputFile Factory Methods
InputFile 工厂方法
csharp
using Appwrite.Models;
InputFile.FromPath("/path/to/file.png") // from filesystem path
InputFile.FromBytes(byteArray, "file.png", "image/png") // from byte[]
InputFile.FromStream(stream, "file.png", "image/png", size) // from Stream (size required)csharp
using Appwrite.Models;
InputFile.FromPath("/path/to/file.png") // 从文件系统路径创建
InputFile.FromBytes(byteArray, "file.png", "image/png") // 从byte[]创建
InputFile.FromStream(stream, "file.png", "image/png", size) // 从Stream创建(需要指定size)Teams
团队管理
csharp
var teams = new Teams(client);
// Create team
var team = await teams.Create(ID.Unique(), "Engineering");
// List teams
var list = await teams.List();
// Create membership (invite user by email)
var membership = await teams.CreateMembership(
teamId: "[TEAM_ID]",
roles: new List<string> { "editor" },
email: "user@example.com"
);
// List memberships
var members = await teams.ListMemberships("[TEAM_ID]");
// Update membership roles
await teams.UpdateMembership("[TEAM_ID]", "[MEMBERSHIP_ID]", new List<string> { "admin" });
// Delete team
await teams.Delete("[TEAM_ID]");Role-based access: Usefor all team members orRole.Team("[TEAM_ID]")for a specific team role when setting permissions.Role.Team("[TEAM_ID]", "editor")
csharp
var teams = new Teams(client);
// 创建团队
var team = await teams.Create(ID.Unique(), "Engineering");
// 列出团队
var list = await teams.List();
// 创建成员身份(通过邮箱邀请用户)
var membership = await teams.CreateMembership(
teamId: "[TEAM_ID]",
roles: new List<string> { "editor" },
email: "user@example.com"
);
// 列出成员身份
var members = await teams.ListMemberships("[TEAM_ID]");
// 更新成员角色
await teams.UpdateMembership("[TEAM_ID]", "[MEMBERSHIP_ID]", new List<string> { "admin" });
// 删除团队
await teams.Delete("[TEAM_ID]");基于角色的访问控制: 设置权限时,使用表示所有团队成员,或使用Role.Team("[TEAM_ID]")表示特定团队角色。Role.Team("[TEAM_ID]", "editor")
Serverless Functions
无服务器函数
csharp
var functions = new Functions(client);
// Execute function
var execution = await functions.CreateExecution("[FUNCTION_ID]", body: "{\"key\": \"value\"}");
// List executions
var executions = await functions.ListExecutions("[FUNCTION_ID]");csharp
var functions = new Functions(client);
// 执行函数
var execution = await functions.CreateExecution("[FUNCTION_ID]", body: "{\"key\": \"value\"}");
// 列出执行记录
var executions = await functions.ListExecutions("[FUNCTION_ID]");Writing a Function Handler (.NET runtime)
编写函数处理程序(.NET运行时)
csharp
// src/Main.cs — Appwrite Function entry point
using System.Text.Json;
public async Task<RuntimeOutput> Main(RuntimeContext context)
{
// context.Req.Body — raw body (string)
// context.Req.BodyJson — parsed JSON (JsonElement)
// context.Req.Headers — headers (Dictionary)
// context.Req.Method — HTTP method
// context.Req.Path — URL path
// context.Req.Query — query params (Dictionary)
context.Log($"Processing: {context.Req.Method} {context.Req.Path}");
if (context.Req.Method == "GET")
return context.Res.Json(new { message = "Hello from Appwrite Function!" });
return context.Res.Json(new { success = true }); // JSON
// context.Res.Text("Hello"); // plain text
// context.Res.Empty(); // 204
// context.Res.Redirect("https://..."); // 302
}csharp
// src/Main.cs — Appwrite 函数入口点
using System.Text.Json;
public async Task<RuntimeOutput> Main(RuntimeContext context)
{
// context.Req.Body — 原始请求体(字符串)
// context.Req.BodyJson — 解析后的JSON(JsonElement)
// context.Req.Headers — 请求头(字典)
// context.Req.Method — HTTP方法
// context.Req.Path — URL路径
// context.Req.Query — 查询参数(字典)
context.Log($"Processing: {context.Req.Method} {context.Req.Path}");
if (context.Req.Method == "GET")
return context.Res.Json(new { message = "Hello from Appwrite Function!" });
return context.Res.Json(new { success = true }); // 返回JSON
// context.Res.Text("Hello"); // 返回纯文本
// context.Res.Empty(); // 返回204无内容
// context.Res.Redirect("https://..."); // 返回302重定向
}Server-Side Rendering (SSR) Authentication
服务器端渲染(SSR)身份验证
SSR apps using .NET frameworks (ASP.NET, Blazor Server, etc.) use the server SDK to handle auth. You need two clients:
- Admin client — uses an API key, creates sessions, bypasses rate limits (reusable singleton)
- Session client — uses a session cookie, acts on behalf of a user (create per-request, never share)
csharp
using Appwrite;
using Appwrite.Services;
// Admin client (reusable)
var adminClient = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject("[PROJECT_ID]")
.SetKey(Environment.GetEnvironmentVariable("APPWRITE_API_KEY"));
// Session client (create per-request)
var sessionClient = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject("[PROJECT_ID]");
var session = Request.Cookies["a_session_[PROJECT_ID]"];
if (session != null)
{
sessionClient.SetSession(session);
}使用.NET框架(ASP.NET、Blazor Server等)的SSR应用通过服务器SDK处理身份验证。你需要两个客户端:
- 管理员客户端 — 使用API密钥,可创建会话,绕过速率限制(可复用单例)
- 会话客户端 — 使用会话Cookie,代表用户执行操作(每个请求创建一个,切勿共享)
csharp
using Appwrite;
using Appwrite.Services;
// 管理员客户端(可复用)
var adminClient = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject("[PROJECT_ID]")
.SetKey(Environment.GetEnvironmentVariable("APPWRITE_API_KEY"));
// 会话客户端(每个请求创建)
var sessionClient = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject("[PROJECT_ID]");
var session = Request.Cookies["a_session_[PROJECT_ID]"];
if (session != null)
{
sessionClient.SetSession(session);
}Email/Password Login (ASP.NET Minimal API)
邮箱/密码登录(ASP.NET Minimal API)
csharp
app.MapPost("/login", async (HttpContext ctx, LoginRequest body) =>
{
var account = new Account(adminClient);
var session = await account.CreateEmailPasswordSession(body.Email, body.Password);
// Cookie name must be a_session_<PROJECT_ID>
ctx.Response.Cookies.Append("a_session_[PROJECT_ID]", session.Secret, new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Strict,
Path = "/",
});
return Results.Ok(new { success = true });
});csharp
app.MapPost("/login", async (HttpContext ctx, LoginRequest body) =>
{
var account = new Account(adminClient);
var session = await account.CreateEmailPasswordSession(body.Email, body.Password);
// Cookie名称必须为a_session_<PROJECT_ID>
ctx.Response.Cookies.Append("a_session_[PROJECT_ID]", session.Secret, new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Strict,
Path = "/",
});
return Results.Ok(new { success = true });
});Authenticated Requests
已验证请求
csharp
app.MapGet("/user", async (HttpContext ctx) =>
{
var session = ctx.Request.Cookies["a_session_[PROJECT_ID]"];
if (session == null) return Results.Unauthorized();
var sessionClient = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject("[PROJECT_ID]")
.SetSession(session);
var account = new Account(sessionClient);
var user = await account.Get();
return Results.Ok(user);
});csharp
app.MapGet("/user", async (HttpContext ctx) =>
{
var session = ctx.Request.Cookies["a_session_[PROJECT_ID]"];
if (session == null) return Results.Unauthorized();
var sessionClient = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject("[PROJECT_ID]")
.SetSession(session);
var account = new Account(sessionClient);
var user = await account.Get();
return Results.Ok(user);
});OAuth2 SSR Flow
OAuth2 SSR 流程
csharp
// Step 1: Redirect to OAuth provider
app.MapGet("/oauth", async () =>
{
var account = new Account(adminClient);
var redirectUrl = await account.CreateOAuth2Token(
provider: OAuthProvider.Github,
success: "https://example.com/oauth/success",
failure: "https://example.com/oauth/failure"
);
return Results.Redirect(redirectUrl);
});
// Step 2: Handle callback — exchange token for session
app.MapGet("/oauth/success", async (HttpContext ctx, string userId, string secret) =>
{
var account = new Account(adminClient);
var session = await account.CreateSession(userId, secret);
ctx.Response.Cookies.Append("a_session_[PROJECT_ID]", session.Secret, new CookieOptions
{
HttpOnly = true, Secure = true, SameSite = SameSiteMode.Strict, Path = "/",
});
return Results.Ok(new { success = true });
});Cookie security: Always use,HttpOnly, andSecureto prevent XSS. The cookie name must beSameSite = SameSiteMode.Strict.a_session_<PROJECT_ID>
Forwarding user agent: Callto record the end-user's browser info for debugging and security.sessionClient.SetForwardedUserAgent(ctx.Request.Headers["User-Agent"])
csharp
// 步骤1:重定向到OAuth提供商
app.MapGet("/oauth", async () =>
{
var account = new Account(adminClient);
var redirectUrl = await account.CreateOAuth2Token(
provider: OAuthProvider.Github,
success: "https://example.com/oauth/success",
failure: "https://example.com/oauth/failure"
);
return Results.Redirect(redirectUrl);
});
// 步骤2:处理回调 — 交换令牌获取会话
app.MapGet("/oauth/success", async (HttpContext ctx, string userId, string secret) =>
{
var account = new Account(adminClient);
var session = await account.CreateSession(userId, secret);
ctx.Response.Cookies.Append("a_session_[PROJECT_ID]", session.Secret, new CookieOptions
{
HttpOnly = true, Secure = true, SameSite = SameSiteMode.Strict, Path = "/",
});
return Results.Ok(new { success = true });
});Cookie安全性: 始终使用、HttpOnly和Secure以防止XSS攻击。Cookie名称必须为SameSite = SameSiteMode.Strict。a_session_<PROJECT_ID>
转发用户代理: 调用以记录终端用户的浏览器信息,用于调试和安全目的。sessionClient.SetForwardedUserAgent(ctx.Request.Headers["User-Agent"])
Error Handling
错误处理
csharp
using Appwrite;
try
{
var row = await tablesDB.GetRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]");
}
catch (AppwriteException e)
{
Console.WriteLine(e.Message); // human-readable message
Console.WriteLine(e.Code); // HTTP status code (int)
Console.WriteLine(e.Type); // error type (e.g. "document_not_found")
Console.WriteLine(e.Response); // full response body
}Common error codes:
| Code | Meaning |
|---|---|
| Unauthorized — missing or invalid session/API key |
| Forbidden — insufficient permissions |
| Not found — resource does not exist |
| Conflict — duplicate ID or unique constraint |
| Rate limited — too many requests |
csharp
using Appwrite;
try
{
var row = await tablesDB.GetRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]");
}
catch (AppwriteException e)
{
Console.WriteLine(e.Message); // 人类可读消息
Console.WriteLine(e.Code); // HTTP状态码(整数)
Console.WriteLine(e.Type); // 错误类型(例如"document_not_found")
Console.WriteLine(e.Response); // 完整响应体
}常见错误码:
| 代码 | 含义 |
|---|---|
| 未授权 — 缺少或无效的会话/API密钥 |
| 禁止访问 — 权限不足 |
| 未找到 — 资源不存在 |
| 冲突 — 重复ID或唯一约束冲突 |
| 请求受限 — 请求过于频繁 |
Permissions & Roles (Critical)
权限与角色(关键)
Appwrite uses permission strings to control access to resources. Each permission pairs an action (, , , , or which grants create + update + delete) with a role target. By default, no user has access unless permissions are explicitly set at the document/file level or inherited from the collection/bucket settings. Permissions are arrays of strings built with the and helpers.
readupdatedeletecreatewritePermissionRolecsharp
using Appwrite;
// Permission and Role are included in the main namespaceAppwrite使用权限字符串控制资源访问。每个权限将操作(、、、或,后者包含create+update+delete)与角色目标配对。默认情况下,所有用户都无访问权限,除非在文档/文件级别显式设置权限,或从集合/存储桶设置继承权限。权限是通过和助手构建的字符串数组。
readupdatedeletecreatewritePermissionRolecsharp
using Appwrite;
// Permission和Role已包含在主命名空间中Database Row with Permissions
带权限的数据库行
csharp
var doc = await tablesDB.CreateRow("[DATABASE_ID]", "[TABLE_ID]", ID.Unique(),
new Dictionary<string, object> { { "title", "Hello World" } },
new List<string>
{
Permission.Read(Role.User("[USER_ID]")), // specific user can read
Permission.Update(Role.User("[USER_ID]")), // specific user can update
Permission.Read(Role.Team("[TEAM_ID]")), // all team members can read
Permission.Read(Role.Any()), // anyone (including guests) can read
});csharp
var doc = await tablesDB.CreateRow("[DATABASE_ID]", "[TABLE_ID]", ID.Unique(),
new Dictionary<string, object> { { "title", "Hello World" } },
new List<string>
{
Permission.Read(Role.User("[USER_ID]")), // 指定用户可读取
Permission.Update(Role.User("[USER_ID]")), // 指定用户可更新
Permission.Read(Role.Team("[TEAM_ID]")), // 所有团队成员可读取
Permission.Read(Role.Any()), // 任何人(包括访客)可读取
});File Upload with Permissions
带权限的文件上传
csharp
var file = await storage.CreateFile("[BUCKET_ID]", ID.Unique(),
InputFile.FromPath("/path/to/file.png"),
new List<string>
{
Permission.Read(Role.Any()),
Permission.Update(Role.User("[USER_ID]")),
Permission.Delete(Role.User("[USER_ID]")),
});When to set permissions: Set document/file-level permissions when you need per-resource access control. If all documents in a collection share the same rules, configure permissions at the collection/bucket level and leave document permissions empty.
Common mistakes:
- Forgetting permissions — the resource becomes inaccessible to all users (including the creator)
withRole.Any()/write/update— allows any user, including unauthenticated guests, to modify or remove the resourcedelete on sensitive data — makes the resource publicly readablePermission.Read(Role.Any())
csharp
var file = await storage.CreateFile("[BUCKET_ID]", ID.Unique(),
InputFile.FromPath("/path/to/file.png"),
new List<string>
{
Permission.Read(Role.Any()),
Permission.Update(Role.User("[USER_ID]")),
Permission.Delete(Role.User("[USER_ID]")),
});何时设置权限: 当需要按资源设置访问控制时,在文档/文件级别设置权限。如果集合中的所有文档共享相同规则,请在集合/存储桶级别配置权限,文档级权限留空即可。
常见错误:
- 忘记设置权限 — 资源对所有用户(包括创建者)都不可访问
- 对
/write/update使用delete— 允许任何用户(包括未认证访客)修改或删除资源Role.Any()- 对敏感数据使用
— 使资源可公开读取Permission.Read(Role.Any())