Create a custom DICOMweb request in C# with Aspose.Medical’s low-level SendAsync API. This tutorial is the client-side companion to Build a Custom DICOMweb Server with Aspose.Medical in C#. That tutorial creates and hosts the server and here, a separate console application sends a QIDO-RS study query to it and prints the returned metadata.
Start the Server
Create the DicomWebServer project and use the complete Program.cs from the server tutorial. From that project directory, start the server:
dotnet run -- --urls http://127.0.0.1:5000
Keep this terminal running. The server exposes its Studies Service at http://127.0.0.1:5000/dicomweb/ and seeds an in-memory study with these values:
| Attribute | Seeded value |
|---|---|
| Patient Name | Doe^Jane |
| Patient ID | DEMO-001 |
| Study Instance UID | 1.2.826.0.1.3680043.10.999.1 |
The client below searches by that Study Instance UID. Evaluation mode can replace demographic values, so the query does not depend on the patient name. No DICOM upload is needed: the server creates the synthetic study at startup. Its data is held in memory, and the seed is recreated on each restart.
In a second terminal, check that the server responds before creating the client:
curl.exe -H "Accept: application/dicom+json" "http://127.0.0.1:5000/dicomweb/studies?StudyInstanceUID=1.2.826.0.1.3680043.10.999.1&limit=5"
The response should contain one study with the UID above. These are synthetic study metadata, not a diagnostic image.
Create the .NET 10 Project
Use the same .NET 10 SDK and Aspose.Medical 26.10.0 as the server tutorial. In the second terminal, create a separate client project alongside DicomWebServer and install the pinned package:
dotnet new console -n CustomDicomWebRequest -f net10.0
cd CustomDicomWebRequest
dotnet add package Aspose.Medical --version 26.10.0
The generated project enables implicit usings and nullable reference types. Its project file should contain:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspose.Medical" Version="26.10.0" />
</ItemGroup>
</Project>
Package Manager Console alternative
To begin, add the Aspose.Medical package to your .NET project using NuGet:
Install-Package Aspose.Medical
The product page and documentation are available at the following links:
- Product page: https://products.aspose.com/medical/net/
- Documentation: https://docs.aspose.com/medical/net/
License and endpoint settings
Both examples load a license when ASPOSE_MEDICAL_LICENSE contains its file path. Set that variable in each terminal to your own license file before starting the corresponding application. Client-side DICOM JSON parsing can be restricted in evaluation mode even when the server returns HTTP 200; a license exception means that the client did not finish reading the result.
The companion server does not configure authentication, so leave DICOMWEB_TOKEN unset for this local example. If you adapt the client to an endpoint that accepts a bearer token, set DICOMWEB_TOKEN to that token. Other authentication arrangements require corresponding transport configuration.
Send a Complete Custom QIDO-RS Request in C#
Replace the client project’s Program.cs with the code below. It defaults to the companion server’s base URI and seeded Study Instance UID. Optional arguments let you query another service or study.
The program explicitly requests DICOM JSON, URL-encodes the UID, requests up to five studies, checks the HTTP status, and disposes of the streamed response. If an attribute is absent, it prints (not returned).
using System.Net.Http.Headers;
using Aspose.Medical.Dicom;
using Aspose.Medical.Dicom.Tags;
using Aspose.Medical.Dicom.Web;
using Aspose.Medical.Dicom.Web.Transactions;
try
{
string? licensePath = Environment.GetEnvironmentVariable("ASPOSE_MEDICAL_LICENSE");
if (!string.IsNullOrWhiteSpace(licensePath))
new Aspose.Medical.License().SetLicense(licensePath);
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/dicom+json"));
string? token = Environment.GetEnvironmentVariable("DICOMWEB_TOKEN");
if (!string.IsNullOrWhiteSpace(token))
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var client = new DicomWebClient(httpClient, new DicomWebClientOptions
{
BaseUri = new Uri(args.Length > 0 ? args[0] : "http://127.0.0.1:5000/dicomweb/", UriKind.Absolute)
});
string studyUid = args.Length > 1 ? args[1] : "1.2.826.0.1.3680043.10.999.1";
var request = new DicomWebClientRequest(SearchStudies.Instance)
{
Query = DicomWebQueryParameters.Parse(
$"StudyInstanceUID={Uri.EscapeDataString(studyUid)}&limit=5")
};
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await using DicomWebClientResponse response = await client.SendAsync(request, timeout.Token);
response.EnsureSuccessStatusCode();
int count = 0;
await foreach (Dataset dataset in response.ReadDatasetsAsync(timeout.Token))
{
string name = dataset.GetValueOrDefault<string>(Tag.PatientName, 0, "(not returned)");
string uid = dataset.GetValueOrDefault<string>(Tag.StudyInstanceUID, 0, "(not returned)");
Console.WriteLine($"Patient: {name}, Study UID: {uid}");
count++;
}
Console.WriteLine($"Studies returned: {count}");
}
catch (Exception ex)
{
Console.Error.WriteLine($"{ex.GetType().Name}: {ex.Message}");
Environment.ExitCode = 1;
}
Run the example
With the server still running, execute this command from CustomDicomWebRequest:
dotnet run
This sends the same study query as the earlier curl.exe command. With a suitable license and the freshly started companion server, the expected output is:
Patient: Doe^Jane, Study UID: 1.2.826.0.1.3680043.10.999.1
Studies returned: 1
Evaluation mode can change the displayed patient name or prevent JSON parsing. To supply the endpoint and study explicitly, use:
dotnet run -- "http://127.0.0.1:5000/dicomweb/" "1.2.826.0.1.3680043.10.999.1"
Follow the request and response flow
DicomWebClientRequest(SearchStudies.Instance) selects the QIDO-RS study-search transaction. DicomWebQueryParameters.Parse supplies StudyInstanceUID and limit=5; SendAsync sends that request with a 30-second cancellation token to /dicomweb/studies on the companion server.
The server’s HTTP adapter removes the /dicomweb prefix and creates a server-side DicomWebRequest with the path /studies. Its DicomWebApplication.ProcessAsync dispatches the query to the Studies Service backed by MemoryStudiesProvider. The adapter writes the response back over HTTP for this client to read. DicomWebClientRequest is the client-side transaction description; DicomWebRequest is the server-side representation of the incoming HTTP request.
After EnsureSuccessStatusCode(), ReadDatasetsAsync yields datasets for asynchronous iteration. The loop reads Tag.PatientName and Tag.StudyInstanceUID, then prints the total number of datasets returned. These are study metadata results, rather than image files.
A successful call can return zero studies when you supply a UID that is not stored on the server. The client’s limit=5 requests at most five results; the companion server also sets MaximumSearchResults = 100. If the program prints an exception and exits with code 1, the query did not complete; an evaluation-license exception is not an empty search result. A connection failure means you should check that the server is running on the configured address.
Conclusion
The two tutorials form a complete local server-and-client example: the companion tutorial hosts the DICOMweb Studies Service, and this console application queries its seeded study. To customize the request, choose a supported transaction, supply its parameters, call SendAsync, and read the corresponding response format.
FAQs
Do I need a DICOM server to run the example? Yes. Start the server from Build a Custom DICOMweb Server in C# and leave it running at
http://127.0.0.1:5000/dicomweb/. The client defaults to that endpoint.Can I use this approach for other DICOMweb transactions? Use a supported transaction from
Aspose.Medical.Dicom.Web.Transactionsand supply its required parameters. Response handling must match that transaction; dataset iteration is the approach shown for this QIDO-RS search.How do I add authentication headers to the request? Set the
HttpClient’sDefaultRequestHeaders(e.g.,Authorization) before creating theDicomWebClient; the client will forward those headers with each request.What format is returned by
ReadDatasetsAsync?ReadDatasetsAsyncprovides a sequence ofDatasetobjects for asynchronous iteration withawait foreach. Each dataset contains DICOM attributes that you can read by tag.Is there a way to limit the number of results returned? Yes, include a
limitparameter in the query string when buildingDicomWebQueryParameters, such as"limit=5".
