Introduction
Setting up a distributed application can involve a surprising number of small manual steps.
A developer may need to import a configuration file, load test data, provide a certificate, select a local settings file, or upload a fixture before a service can start working correctly.
Usually, these tasks are documented as instructions:
1. Find the file.
2. Copy it to a specific folder.
3. Rename it.
4. Start the application.
5. Run the import command.
The process works, but it puts the responsibility on the developer to know exactly what to do.
.NET Aspire 13.5 introduces file-input support for interactive resource commands. This allows an Aspire application to request a file directly through the dashboard, apply restrictions such as file type and size, and pass the selected file to the AppHost for processing. Microsoft specifically highlights file import as one of the new interaction capabilities in Aspire 13.5.
This is a small feature, but it can make a noticeable difference to developer experience because setup operations can become part of the application rather than a separate collection of manual instructions.
What Are Aspire File Inputs?
File inputs allow a custom Aspire resource command to ask the developer to select a file.
The basic workflow is:
Aspire Dashboard
|
v
Resource Command
|
v
File Picker
|
v
Developer Selects File
|
v
AppHost Receives File
|
v
Validate
|
v
Process
|
v
Show Result
For example, a developer could select:
config.json
from the dashboard and have the AppHost import it.
The feature is especially useful for development-time workflows where manually copying files is inconvenient.
Why File Inputs Are Useful
Consider a distributed application with:
Orders API
Inventory API
Payments API
PostgreSQL
Redis
A developer wants to load sample inventory data.
Without an interactive file input:
Download sample-data.json
|
v
Find local project directory
|
v
Copy file
|
v
Run import command
|
v
Check logs
With a dashboard command:
Inventory API
|
+-- Import Sample Data
|
v
Select File
|
v
Validate
|
v
Import
The second workflow is easier to discover, especially for developers who are new to the repository.
File Inputs Are a Development Experience Feature
It is important to understand where this capability fits.
The file-input API is primarily useful for development-time and AppHost workflows.
It does not mean an ASP.NET Core application should expose arbitrary filesystem access through a web interface.
The intended model is closer to:
Developer
|
v
Aspire Dashboard
|
v
AppHost Interaction
|
v
Development Resource
rather than:
Internet User
|
v
Upload API
|
v
Production File System
That distinction matters for security and architecture.
A Typical File Import Scenario
Imagine an application that needs a local JSON configuration file.
The desired user experience could be:
+--------------------------------------+
| Import Configuration |
| |
| Select configuration file |
| |
| [ Choose File ] |
| |
| Accepted: .json |
| Maximum size: 5 MB |
| |
| [ Import ] |
+--------------------------------------+
After the user selects the file:
File
|
v
Read Stream
|
v
Validate Size
|
v
Validate Format
|
v
Parse JSON
|
v
Validate Schema
|
v
Apply Configuration
The important part is that file selection is only the first step.
The application still needs to validate the content.
File Type Restrictions
A useful file-input workflow should restrict the files that developers can select.
For example:
Accepted files:
.json
.yaml
.yml
If the workflow only expects JSON, there is little reason to allow every file type.
Conceptually:
Allowed:
config.json
Rejected:
image.png
archive.zip
unknown.exe
This improves the user experience and reduces accidental input.
However, file extension filtering is not a security boundary.
A malicious or incorrect file can still have a misleading extension.
The application should inspect and validate the actual content.
File Size Limits
File size should also be controlled.
Suppose the workflow only needs a small configuration file.
A sensible limit might be:
Maximum:
5 MB
The exact limit depends on the workflow.
The reason is simple: an interactive file upload should not allow an accidentally huge file to consume unnecessary memory, disk space, or processing time.
A robust processing pipeline is:
Selected File
|
v
Size Check
|
+---- Too Large ----> Reject
|
v
Format Check
|
+---- Invalid ------> Reject
|
v
Content Validation
|
+---- Invalid ------> Reject
|
v
Process
Microsoft's Aspire 13.5 release notes specifically describe file-input capabilities that include file-type restrictions and maximum file-size limits.
Working With the Uploaded File
The AppHost receives the selected file as a stream.
A simplified processing method could look like:
public async Task ImportConfigurationAsync(Stream file)
{
using var reader = new StreamReader(file);
var json = await reader.ReadToEndAsync();
if (string.IsNullOrWhiteSpace(json))
{
throw new InvalidOperationException(
"The configuration file is empty.");
}
// Parse and validate the configuration.
}
The important point is to process the stream rather than assuming that the file already exists somewhere on the developer's machine.
The AppHost becomes responsible for handling the file received from the interaction.
Deserialize Only After Validation
Suppose the expected configuration looks like:
{
"environment": "Development",
"apiBaseUrl": "https://localhost:5001",
"enableSeedData": true
}
A corresponding model might be:
public sealed class AppConfiguration
{
public string Environment { get; set; } = string.Empty;
public string ApiBaseUrl { get; set; } = string.Empty;
public bool EnableSeedData { get; set; }
}
The file can then be deserialized:
var configuration =
JsonSerializer.Deserialize<AppConfiguration>(
json);
if (configuration is null)
{
throw new InvalidOperationException(
"Invalid configuration.");
}
But deserialization alone is not validation.
You should still verify required values:
if (string.IsNullOrWhiteSpace(configuration.Environment))
{
throw new InvalidOperationException(
"Environment is required.");
}
if (string.IsNullOrWhiteSpace(configuration.ApiBaseUrl))
{
throw new InvalidOperationException(
"API base URL is required.");
}
This prevents an apparently valid JSON document from becoming invalid application state.
File Inputs and Configuration Import
One practical use case is importing local environment configuration.
For example:
Development
|
+-- appsettings.json
+-- appsettings.Development.json
+-- local-secrets.json
Instead of asking every developer to manually copy these files, Aspire can provide an explicit import workflow.
The command could be:
Orders API
|
+-- Import Local Configuration
The developer selects the file.
The AppHost validates it.
The application then receives the required development configuration.
This can make onboarding considerably easier.
File Inputs and Test Data
Another useful scenario is loading test data.
Imagine an e-commerce application that needs a large product dataset for local development.
The file could contain:
{
"products": [
{
"name": "Laptop",
"price": 1200
},
{
"name": "Keyboard",
"price": 80
}
]
}
The workflow becomes:
Select products.json
|
v
Validate JSON
|
v
Validate Product Schema
|
v
Insert Test Data
|
v
Show Import Result
This is much easier than asking developers to learn a database import command before they can start testing the application.
Showing Import Progress
File imports can take time.
For example:
products.json
|
v
10,000 records
|
v
Validate
|
v
Transform
|
v
Insert
A progress workflow can make the operation easier to understand.
Conceptually:
Importing products...
[############--------] 63%
6,300 / 10,000 records
Aspire 13.5 also adds progress-dialog support to the Interaction Service. Microsoft identifies the progress-dialog functionality as experimental in this release, so teams should evaluate it carefully before depending on it as a stable API.
File Inputs vs Command-Line Arguments
Developers sometimes need to decide whether a workflow should use a file picker or a command-line argument.
Both can be useful.
| Requirement | File Input | Command Argument |
|---|
| Developer selects a local file | Excellent | Moderate |
| Dashboard-friendly | Excellent | Good |
| Script-friendly | Limited | Excellent |
| CLI automation | Limited | Excellent |
| File-specific UI | Excellent | No |
| Easy for beginners | Excellent | Moderate |
| Repeatable automation | Moderate | Excellent |
A good approach is to support both where the workflow needs both interactive and automated usage.
For example:
Dashboard:
Import Data → Choose File
CLI:
Import Data --file products.json
Aspire's interaction documentation recommends command arguments for scenarios where command input needs to work across both dashboard and CLI contexts.
File Inputs vs Web Upload APIs
These two concepts should not be confused.
A production upload API might look like:
Browser
|
v
POST /api/files
|
v
ASP.NET Core
|
v
Object Storage
An Aspire development-time file input looks more like:
Developer
|
v
Aspire Dashboard
|
v
AppHost
|
v
Development Resource
The first is part of an application's user-facing architecture.
The second is part of the developer experience.
This distinction helps prevent accidentally putting development-only behavior into production APIs.
Testing File Inputs
A useful test plan should cover more than successful imports.
Test 1: Valid File
config.json
|
v
Accepted
|
v
Imported
Test 2: Wrong Extension
config.exe
|
v
Rejected
Test 3: Oversized File
50 MB
|
v
Maximum = 5 MB
|
v
Rejected
Test 4: Invalid JSON
config.json
|
v
Malformed JSON
|
v
Rejected
Test 5: Valid JSON but Invalid Schema
{
"environment": 123
}
The file is valid JSON but does not satisfy the application's expected structure.
It should be rejected.
Test 6: Empty File
0 bytes
|
v
Rejected
Test 7: Large Valid Dataset
Test a realistic file size and record count.
This is important because a workflow that works with 20 records may behave very differently with 100,000 records.
A Practical Validation Pipeline
A production-quality development import can follow this structure:
public async Task ImportAsync(Stream file)
{
// 1. Validate the input stream.
if (file is null)
{
throw new ArgumentNullException(nameof(file));
}
// 2. Read the content.
using var reader = new StreamReader(file);
var json = await reader.ReadToEndAsync();
// 3. Validate content.
if (string.IsNullOrWhiteSpace(json))
{
throw new InvalidOperationException(
"The selected file is empty.");
}
// 4. Deserialize.
var model = JsonSerializer.Deserialize<AppConfiguration>(
json);
if (model is null)
{
throw new InvalidOperationException(
"The file contains invalid configuration.");
}
// 5. Validate business rules.
Validate(model);
// 6. Apply the configuration.
await ApplyConfigurationAsync(model);
}
The important design principle is that the interaction layer should not become responsible for business rules.
It should collect the file.
The application should validate and process it.
Keeping File Processing Out of the AppHost
An AppHost can become difficult to maintain if all import logic is placed directly inside it.
A better design is:
AppHost
|
+--> Interaction
|
+--> File Selection
|
v
Configuration Service
|
+--> Validation
+--> Parsing
+--> Transformation
+--> Import
For example:
public interface IConfigurationImporter
{
Task ImportAsync(
Stream file,
CancellationToken cancellationToken);
}
Then the AppHost simply coordinates the interaction.
This keeps the actual processing logic reusable and testable.
Handling Errors Gracefully
A failed file import should provide a useful message.
Bad:
Operation failed.
Better:
Import failed.
The selected configuration is missing
the required "apiBaseUrl" property.
Even better, if appropriate:
Import failed.
Missing property:
apiBaseUrl
Expected:
string URL
Good error messages reduce debugging time.
Common Mistakes
Mistake 1: Treating File Extension as Validation
A file called:
config.json
is not automatically valid JSON.
Always validate the content.
Mistake 2: No File Size Limit
Large files can consume unnecessary resources.
Set an appropriate maximum size for the workflow.
Mistake 3: Reading Everything Into Memory
For small configuration files this may be acceptable.
For large datasets, streaming processing may be more appropriate.
Mistake 4: Putting Business Logic in the AppHost
Keep parsing and domain-specific processing in dedicated services.
Mistake 5: Assuming Dashboard Input Works Everywhere
Dashboard interactions and CLI workflows have different capabilities.
Use command arguments when automation is important.
Mistake 6: Treating Experimental APIs as Stable
Some Aspire 13.5 interaction features are experimental. Verify the current API and diagnostics before building a long-lived internal framework around them.
Troubleshooting
| Problem | What to Check |
|---|
| File picker does not appear | Verify the resource command and file-input configuration |
| File rejected unexpectedly | Check accepted extensions and size limits |
| Import fails | Validate file content and schema |
| JSON parsing fails | Check encoding and document structure |
| Large file causes slow processing | Consider streaming and batching |
| CLI workflow does not work | Use command arguments where appropriate |
| Progress UI does not behave as expected | Check experimental API requirements |
| AppHost becomes difficult to maintain | Move processing into a dedicated service |
| File works locally but not in another environment | Check environment-specific paths and dependencies |
Advantages
Better Developer Onboarding
New developers can discover setup operations from the Aspire dashboard instead of reading a long setup document.
Fewer Manual File-Copy Steps
Developers do not need to remember where configuration or test files belong.
Clearer Development Workflows
The dashboard can expose operations such as:
Import Configuration
Import Test Data
Load Fixture
Reset Environment
Better Validation
The workflow can validate file type, size, schema, and content before applying changes.
Good Fit for Distributed Applications
File-based setup can be associated directly with the resource that needs it.
Disadvantages and Limitations
Development-Focused
This capability is primarily useful for developer workflows and should not automatically be treated as a replacement for production upload APIs.
Experimental Features
Some related interaction capabilities in Aspire 13.5 are experimental.
Additional AppHost Complexity
Interactive commands add code and configuration to the AppHost.
File Processing Still Requires Engineering
Selecting a file does not solve validation, parsing, schema management, or data-import problems.
Dashboard and CLI Differences
A workflow designed only around dashboard interactions may not translate directly to automated CLI scenarios.
Best Practices
Keep File Inputs Purpose-Specific
Do not create a generic "Upload Anything" command.
Prefer:
Import Configuration
or:
Load Product Test Data
The purpose should be clear.
Restrict File Types
Only accept formats the operation understands.
Set a Reasonable Size Limit
Do not allow unnecessarily large files.
Validate Content
Check structure, required fields, data types, and business rules.
Keep Processing Logic Separate
Use dedicated services rather than placing all import logic inside the AppHost.
Provide Useful Errors
Tell developers what went wrong and how to fix it.
Support Automation When Needed
If the workflow needs to run in CI or through the CLI, consider command arguments instead of relying exclusively on interactive file selection.
Test Realistic Files
Use both small and representative production-like development datasets.
A Practical Aspire Setup
A simplified architecture could look like:
DistributedApplication
|
v
Orders API
|
+-- Import Configuration
|
+-- Load Test Data
|
v
Interaction Service
|
v
File Input
|
v
ConfigurationImporter
|
+-- Validate
+-- Parse
+-- Transform
+-- Apply
The AppHost provides the interaction.
The importer owns the actual application logic.
That separation keeps the design easier to maintain.
Conclusion
.NET Aspire 13.5's file-input capability is a small feature with a practical benefit: it can turn manual development setup steps into simple, discoverable workflows inside the Aspire dashboard. Instead of telling every developer where to copy a configuration file or how to run a data-import command, the application can provide an "Import" action and guide them through the process. The important part is not to stop at the file picker. Files still need size limits, format checks, schema validation, and proper error handling. It is also worth remembering that dashboard interactions and CLI workflows are not identical, so command arguments may be a better option when automation is required. Used thoughtfully, file inputs can make distributed .NET projects easier to set up and much more comfortable to work with during day-to-day development.