用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/midudev/autoskills --skill minimal-api-file-upload命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Production patterns for FastMCP Python MCP servers. Use when writing, improving, or debugging FastMCP Python code — tool schema design, multi-operation tools, pre-formatted output, Context usage, middleware, lifespan and startup/shutdown hooks, partial failure handling, running and deploying, async patterns, in-process testing with fastmcp.Client, and common mistakes. Do NOT activate for general questions about what MCP is, MCP concepts, or building an MCP server from scratch (use mcp-builder for that).
Create backend with ElysiaJS, a type-safe, high-performance framework.
Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines, dependsOn, caching, remote cache, the "turbo" CLI, --filter, --affected, CI optimization, environment variables, internal packages, monorepo structure/best practices, and boundaries. Use when user: configures tasks/workflows/pipelines, creates packages, sets up monorepo, shares code between apps, runs changed/affected packages, debugs cache, or has apps/packages directories.
基于 SOC 职业分类
正在显示 SKILL.md
| name | minimal-api-file-upload |
| description | File upload endpoints in ASP.NET minimal APIs (.NET 8+) |
| license | MIT |
[FromForm] IFormFile works directly with attributesMultipartReader instead| Input | Required | Description |
|---|---|---|
| File parameter(s) | Yes | IFormFile or IFormFileCollection |
| Size limits | Yes | Max file/request size |
| Allowed types | No | Content type or extension restrictions |
// In .NET 8+ minimal APIs, IFormFile binds automatically from multipart/form-data
// when it is the only complex parameter.
app.MapPost("/upload", (IFormFile file) => ...);
// CRITICAL: When you mix files with other form fields, use [FromForm] on all
// form-bound parameters (or group them into a single [FromForm] DTO).
app.MapPost("/upload-with-metadata",
([FromForm] IFormFile file, [FromForm] string description) =>
{
return Results.Ok(new { file.FileName, Description = description });
});
// Multiple files: IFormFileCollection also binds automatically from multipart/form-data.
// You only need [FromForm] if you mix it with other form fields, as shown above.
app.MapPost("/upload-multiple", (IFormFileCollection files) =>
{
return Results.Ok(files.Select(f => new { f.FileName, f.Length }));
});
// CRITICAL: There are TWO different size limits and you need to configure BOTH
// 1. Request body size limit (Kestrel level) — default is 30MB
builder.WebHost.ConfigureKestrel(options =>
{
options.Limits.MaxRequestBodySize = 10 * 1024 * 1024; // 10 MB
});
// 2. Form options — multipart body length limit — default is 128MB
builder.Services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = 10 * 1024 * 1024; // 10 MB
options.ValueLengthLimit = 1024 * 1024; // 1 MB for form values
options.MultipartHeadersLengthLimit = 16384; // 16 KB for section headers
});
// COMMON MISTAKE: Only increasing Kestrel MaxRequestBodySize
// upload still fails because FormOptions.MultipartBodyLengthLimit is exceeded
// COMMON MISTAKE: Only increasing FormOptions
// upload fails with "Request body too large" from Kestrel before reaching form parsing
// CRITICAL: Per-endpoint override with RequestSizeLimit attribute
app.MapPost("/upload-large", [RequestSizeLimit(200_000_000)] (IFormFile file) =>
{
return Results.Ok(new { file.FileName, file.Length });
});
// CRITICAL: To disable the limit entirely (for streaming):
app.MapPost("/upload-unlimited", [DisableRequestSizeLimit] async (HttpContext context) =>
{
// Handle manually
});
// CRITICAL: In .NET 8+ with UseAntiforgery(), ALL form-bound endpoints
// automatically validate anti-forgery tokens, INCLUDING file uploads
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
// This endpoint now REQUIRES an anti-forgery token:
app.MapPost("/upload", (IFormFile file) => Results.Ok(file.FileName));
// Without the token → 400 Bad Request
// CRITICAL: For API-only file uploads (no anti-forgery needed), opt out:
app.MapPost("/api/upload", (IFormFile file) => Results.Ok(file.FileName))
.DisableAntiforgery(); // CRITICAL: Must explicitly opt out
// COMMON MISTAKE: Getting 400 errors on file uploads and not realizing
// it's because UseAntiforgery() is in the pipeline
// WARNING: DisableAntiforgery() is safe for unauthenticated endpoints and
// endpoints using JWT bearer authentication. However, for endpoints
// authenticated with cookies, disabling antiforgery removes CSRF protection
// and exposes the endpoint to cross-site request forgery attacks.
// For cookie-authenticated endpoints, include a valid antiforgery token instead.
app.MapPost("/upload", async (IFormFile file) =>
{
// CRITICAL: Check content type AND file signature (magic bytes)
// NEVER trust file extension alone — it can be spoofed
// Allow only JPEG/PNG by default. To support more (e.g., GIF),
// add the MIME type here AND validate its magic bytes below.
var allowedTypes = new[] { "image/jpeg", "image/png" };
if (!allowedTypes.Contains(file.ContentType, StringComparer.OrdinalIgnoreCase))
return Results.BadRequest("File type not allowed");
// CRITICAL: Check magic bytes for file type verification
using var stream = file.OpenReadStream();
var header = new byte[8];
var bytesRead = await stream.ReadAsync(header, 0, header.Length);
if (bytesRead < 4)
return Results.BadRequest("File content is too short or invalid");
// JPEG: FF D8 FF
// PNG: 89 50 4E 47
var isJpeg = header[0] == 0xFF && header[1] == 0xD8 && header[2] == 0xFF;
var isPng = header[0] == 0x89 && header[1] == 0x50 && header[2] == && header[] == ;
? detectedContentType = isJpeg ? : isPng ? : ;
(detectedContentType )
Results.BadRequest();
(!.Equals(.ContentType, detectedContentType, StringComparison.OrdinalIgnoreCase))
Results.BadRequest();
extension = detectedContentType == ? : ;
safeFileName = ;
filePath = Path.Combine(, safeFileName);
Directory.CreateDirectory();
stream.Position = ;
fileStream = File.Create(filePath);
stream.CopyToAsync(fileStream);
Results.Ok( { FileName = safeFileName, .Length });
});
// CRITICAL: IFormFile relies on multipart form parsing that buffers content in memory
// (up to a threshold) then spills to temp files on disk. For very large uploads,
// this overhead is unnecessary if you can process the data in chunks.
// Use MultipartReader to stream directly — e.g., to a final storage location —
// without buffering the entire file first.
app.MapPost("/upload-stream",
[DisableRequestSizeLimit]
async (HttpContext context) =>
{
// Extract the multipart boundary from the Content-Type header
var contentType = context.Request.ContentType;
if (contentType == null)
return Results.BadRequest("Missing Content-Type");
// Safely parse the Content-Type header to avoid FormatException from MediaTypeHeaderValue.Parse
if (!MediaTypeHeaderValue.TryParse(contentType, out var mediaType))
return Results.BadRequest("Invalid Content-Type");
var boundary = HeaderUtilities.RemoveQuotes(mediaType.Boundary).Value;
if (string.IsNullOrWhiteSpace(boundary))
return Results.BadRequest("Not a multipart request");
var reader = new MultipartReader(boundary, context.Request.Body);
// CRITICAL: ReadNextSectionAsync returns null when there are no more sections
while (await reader.ReadNextSectionAsync() is { } section)
{
// Parse Content-Disposition to identify file sections
if (!ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition))
;
(contentDisposition.DispositionType.Equals()
&& !.IsNullOrEmpty(contentDisposition.FileName.Value))
{
originalFileName = contentDisposition.FileName.Value ?? .Empty;
sanitizedFileName = Path.GetFileName(originalFileName.Trim());
safeFile = ;
Directory.CreateDirectory();
fileStream = File.Create(Path.Combine(, safeFile));
section.Body.CopyToAsync(fileStream);
}
}
Results.Ok();
}).DisableAntiforgery();
MaxRequestBodySize AND FormOptions.MultipartBodyLengthLimit.UseAntiforgery() auto-validates form uploads. Use .DisableAntiforgery() for API endpoints (safe for JWT/unauthenticated; do NOT disable for cookie-authenticated endpoints).Guid.NewGuid() and derive the extension from validated content.MultipartReader to stream data in chunks directly to storage without buffering the entire file.Path.GetExtension(file.FileName). If the original extension must be preserved, validate it against the detected content type.