In DicomObjects .NET 8 version, users can define their own self-hosted server start up and use the middleware they design. An example is where the server wish to route legacy WADO-URI requests to the correct WADO controlers.

Sample custom Startup class:

public class DicomWebStartup : DicomObjects.DicomWeb.IStartup
{
    public void Configure(IApplicationBuilder app)
    {
        // 1. Intercept legacy WADO-URI requests
        app.Use(async (context, next) =>
        {
            var path = context.Request.Path.Value?.ToLowerInvariant();
            if (path == "/wado" &&
                context.Request.Query.TryGetValue("requestType", out var requestType) &&
                string.Equals(requestType, "WADO", StringComparison.OrdinalIgnoreCase))
            {
                // extract parameters
                var studyUid = context.Request.Query["studyUID"].ToString();
                var seriesUid = context.Request.Query["seriesUID"].ToString();
                var objectUid = context.Request.Query["objectUID"].ToString();
                var frameNumber = context.Request.Query["frameNumber"].ToString().Trim();
                if (string.IsNullOrEmpty(frameNumber)) frameNumber = "1";
                var contentType = context.Request.Query["contentType"].ToString().Trim();
                var rows = context.Request.Query["rows"].ToString().Trim();
                var columns = context.Request.Query["columns"].ToString().Trim();

                // basic validation
                if (string.IsNullOrWhiteSpace(studyUid) ||
                    string.IsNullOrWhiteSpace(seriesUid) ||
                    string.IsNullOrWhiteSpace(objectUid))
                {
                    context.Response.StatusCode = 400;
                    await context.Response.WriteAsync("Missing required UIDs");
                    return;
                }

                // build internal route for WADO-RS
                var newPath = $"/wado/studies/{studyUid}/series/{seriesUid}/instances/{objectUid}";                    
                newPath += $"/frames/{frameNumber}";

                // rewrite path and set Accept header
                if (!string.IsNullOrEmpty(rows) && !string.IsNullOrEmpty(columns)) // assuming thumbnail
                {
                    context.Request.Path = newPath + "/thumbnail"; // /thumbnail?viewport=rows,columns will not hit the controller, use querystring instead
                    context.Request.QueryString =
                        new QueryString($"?viewport={rows},{columns}");
                }
                else
                    context.Request.Path = newPath + "/rendered"; // append /rendered so it tells DicomObjects what the wado operation this is

                string path1 = context.Request.Path.Value;

                if (!string.IsNullOrEmpty(contentType))
                    context.Request.Headers["Accept"] = contentType;
                else
                    context.Request.Headers["Accept"] = "image/jpeg"; // default to image/jpeg if this is what the client expects to receive in response

                // this then calls the DicomWebServer's Wado Controller and will fire your WadoReceived event handler
                await next.Invoke();
                return;
            }

            // Otherwise continue normal pipeline
            await next.Invoke();
        });

        // 2. Normal ASP.NET Core pipeline (routing, controllers, etc.)
        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

    /// <summary>
    /// Configure all required services
    /// </summary>
    /// <param name="services">The IService collection</param>
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers().AddControllersAsServices();
    }
}

Sample user code:

Serer Side

const string serverURL = "http://localhost:9090";
DicomWebServer server = new();
Action<ILoggingBuilder> loggingProvider = loggingBuilder =>
{
    loggingBuilder.ClearProviders();
    loggingBuilder.AddConsole();
    loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Error);
};

// use custom web server start up in the Listen method
_ = server.Listen(serverURL, "", "", false, new DicomWebStartup(), loggingProvider);
_ = server.Listen(asyncServerURL, "", "", true, new DicomWebStartup(), loggingProvider);

// set up DicomWebServer's event handler for WADO
server.WadoReceived += Server_WadoReceived;

Client Side

Use postman or any http client, and construct a WADO-URI request similar to the following

http://localhost:9090/wado?requestType=WADO&studyUID={studyUID}&seriesUID={seriesUID}&objectUID={instanceUID}&rows=128&columns=128