Practical Exploration of File Upload Handling in ASP.NET Core Web API
TLDR
- It is recommended to adopt an architecture that "separates file uploads from business logic": upload the file first to obtain an ID, then submit the ID along with the business data.
- Avoid mixing file and business data processing in the same API to maintain consistency in API formatting.
- Use
[FromForm]for file uploads and[FromBody]for JSON business data; these should be designed separately. - File downloads should be integrated into business logic Controllers to implement fine-grained permission control.
- For temporary files not yet bound to business data, implement scheduled tasks to clean them up periodically to avoid wasting storage space.
Development Patterns and Data Reception Methods
In ASP.NET Core, there are two main scenarios for handling file uploads:
- Full-stack Development: Typically uses HTML form submission, with the backend receiving data via
[FromForm]. - Decoupled Frontend/Backend: Typically uses AJAX to transmit JSON, with the backend receiving data via
[FromBody].
Technical Pain Points of the Two Main Approaches
When do you encounter this issue: When developers attempt to process both files and complex business data within the same API.
Using Form Submit (
[FromForm]):- Pain Point: ASP.NET Core uses different
ModelBindermechanisms for[FromBody]and[FromForm]. If you need to handle custom types, you must implement bothModelBinderandJsonConverter, increasing maintenance costs. - Impact: During frontend-backend collaboration, if an API suddenly changes from JSON to FormData, the frontend transmission logic requires significant refactoring.
- Pain Point: ASP.NET Core uses different
Using JSON to transmit Base64 strings (
[FromBody]):- Pain Point: Data transmission volume increases by approximately 1.33 times.
- Impact: During large file transfers, this may trigger performance bottlenecks.
Recommended Solution: Separated Design for File Upload and Business Data Submission
To resolve the conflicts mentioned above, it is recommended to split file uploads and business logic into two independent steps.
Implementation Workflow
- File Upload API: The frontend uses
[FromForm]to upload the file, and the backend returns a file ID after storage. - Business Data API: When creating or modifying business data, the frontend only transmits the file ID.
- Data Management:
- The main business data table stores the file ID, or handles one-to-many relationships via an association table.
- Establish a scheduled task to periodically clean up temporary files that have not been activated for more than a day.
File Data Validation
If you need to check file information during the validation phase, you can obtain services within a ValidationAttribute via Dependency Injection (DI):
protected override ValidationResult IsValid(
object value, ValidationContext validationContext
) {
using IServiceScope scope = validationContext.CreateScope();
IFileService service = scope.ServiceProvider.GetRequiredService<IFileService>();
// Execute validation logic
}Important Notes
File type validation (such as file extensions) is easily tampered with. It is recommended to use it only as a preliminary filter; core security checks should include file signature identification or content scanning.
Extended Discussion: Considerations for File Download Implementation
The implementation method for file downloads depends on permission control requirements:
- Generic File Download API: Simple to implement, but difficult to enforce fine-grained permission checks for specific business scenarios.
- Business Logic Controller Download: Although more complex to implement, it ensures that every file download undergoes proper business permission verification.
Conclusion: It is recommended to integrate download logic into business Controllers and encapsulate repetitive download functionality into a shared service to balance permission security with code reusability.
Change Log
- 2025-03-10 Initial version of the document created.
- 2026-05-21 Added link to GitHub sample project.