Mock Azure Blob Storage With Azurite In ASP.NET Core

I needed a local Blob Storage mock so I could test file uploads without touching a real Azure account. This is the exact setup I use for an ASP.NET Core MVC app like this chatbot project, focused only on Azure Blob.

1. Start Azurite with Docker

1
2
3
4
5
6
docker run -d \
--name azurite \
-p 10000:10000 \
-p 10001:10001 \
-p 10002:10002 \
mcr.microsoft.com/azure-storage/azurite

For Blob Storage specifically, the important endpoint is 10000. Queues and Tables are mapped too, but you can ignore those for this use case.

Docs: Use the Azurite emulator for local Azure Storage development

2. Configure appsettings for Blob only

In this codebase, uploads are abstracted behind IClientFileStorage and configured via AzureBlobStorage settings. For local development, point that section to Azurite.

1
2
3
4
5
6
7
{
"AzureBlobStorage": {
"ConnectionString": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=<azurite-account-key>;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;",
"AccountName": "devstoreaccount1",
"ContainerName": "chatbot-files"
}
}

A couple of practical notes:

  • Keep ContainerName stable (chatbot-files in this app).
  • Use HTTP locally for Azurite (DefaultEndpointsProtocol=http).
  • Keep real cloud credentials out of local files and use placeholders in examples.

Docs: Configure Azure Storage connection strings

3. Register Blob storage in Program.cs

This app binds the options and injects a storage implementation once at startup.

1
2
3
4
builder.Services.Configure<AzureBlobStorageOptions>(
builder.Configuration.GetSection(AzureBlobStorageOptions.SectionName));

builder.Services.AddSingleton<IClientFileStorage, AzureBlobClientFileStorage>();

That keeps controllers clean: they depend on IClientFileStorage and do not care if storage is Azurite or real Azure.

Docs: Options pattern in ASP.NET CoreDependency injection in ASP.NET Core

4. Use the same upload flow as production

The key part I like here is that local and cloud use the same upload code path:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public async Task UploadAsync(Guid clientUid, string fileName, Stream content, string? contentType, CancellationToken cancellationToken = default)
{
var sanitizedFileName = Path.GetFileName(fileName).Trim();
var blobClient = containerClient.GetBlobClient($"{clientUid:D}/{sanitizedFileName}");

await blobClient.UploadAsync(
content,
new BlobUploadOptions
{
HttpHeaders = new BlobHttpHeaders
{
ContentType = string.IsNullOrWhiteSpace(contentType)
? "application/octet-stream"
: contentType
}
},
cancellationToken);
}

In this project, Step Two upload requests end up here, so when I test locally I verify the blob path shape is:

  • chatbot-files/<client-uid>/<file-name>

That gives me realistic behavior while still running fully local.

Docs: Azure.Storage.Blobs client library for .NET

5. Quick verification

After starting the web app and uploading a file from the onboarding screen, I run one of these checks:

1
2
3
4
5
6
# Option A: use Storage Explorer connected to Azurite
# Option B: use Azure CLI against the local endpoint
az storage blob list \
--connection-string "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=<azurite-account-key>;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;" \
--container-name chatbot-files \
--output table

If uploads succeed and blobs appear under the client UID prefix, my local mock is wired correctly.

Docs: Manage Azurite with Storage Exploreraz storage blob list