Skip to content

HTTP

HTTP requests are basically a must in every app. We didn’t want people to have to generate their API client, contracts, and then have to global add the boilerplate mediator contract and subsequent request handler, so… we found some very convenient ways to build this in.

  1. Let’s start with our mediator request contract - Notice that we have some attributes & interfaces that are specific to HTTP requests

    using Shiny.Mediator;
    using Shiny.Mediator.Http;
    [Http(HttpVerb.Post, "/route/{Parameter}")]
    public class MyRequest : IHttpRequest<MyResponse>
    {
    // name of the route parameter
    [HttpParameter(HttpParameterType.Path)]
    public string Parameter { get; set; }
    // query string parameter appended to uri
    [HttpParameter(HttpParameterType.Query)]
    public string QueryValue { get; set; }
    // added to headers
    [HttpParameter(HttpParameterType.Header)]
    public string SomeHeaderValue { get; set; }
    // there can only be one body - serialized to json
    [HttpBody]
    public SomeOtherClass Body { get; set; }
    }
  2. Next, let’s create our configuration for the base URI. This assumes you use Microsoft.Extensions.Configuration JSON, but any other configuration provider with the same structure will work.

    {
    "Mediator": {
    "Http": {
    "Your.Namespace.Contract": "https://yourapi.com",
    // OR
    "Your.Namespace.*": "https://yourapi.com"
    // OR
    "*": "https://yourapi.com"
    }
    }
    }
  3. Now - let’s make the request with mediator

    var response = await mediator.Send(new MyRequest
    {
    Parameter = "someValue",
    QueryValue = "someQuery",
    SomeHeaderValue = "someHeader",
    Body = new SomeOtherClass
    {
    SomeProperty = "someValue"
    }
    });

Decorating the HTTP Request

A common scenario is having to refresh an access token, add it to the next HTTP request, and maybe add some additional header while you’re there. Shiny Mediator allows you to register a different type of middleware of HTTP Requests called IHttpRequestDecorator.

Here’s an example that already exists in Shiny.Mediator.Maui that adds some headers to the request.

Decorators apply to all HTTP requests

public class MauiHttpRequestDecorator<TRequest, TResult>(
IConfiguration configuration,
IAppInfo appInfo,
IDeviceInfo deviceInfo,
IGeolocation geolocation
) : IHttpRequestDecorator<TRequest, TResult> where TRequest : IHttpRequest<TResult>
{
public async Task Decorate(HttpRequestMessage httpMessage, TRequest request)
{
httpMessage.Headers.Add("AppId", appInfo.PackageName);
httpMessage.Headers.Add("AppVersion", appInfo.Version.ToString());
httpMessage.Headers.Add("DeviceManufacturer", deviceInfo.Manufacturer);
httpMessage.Headers.Add("DeviceModel", deviceInfo.Model);
httpMessage.Headers.Add("DevicePlatform", deviceInfo.Platform.ToString());
httpMessage.Headers.Add("DeviceVersion", deviceInfo.Version.ToString());
httpMessage.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(CultureInfo.CurrentCulture.Name));
if (configuration["Mediator:Http:GpsHeader"] == "true")
{
var gps = await geolocation.GetLastKnownLocationAsync();
if (gps != null)
httpMessage.Headers.Add("GpsCoords", $"{gps.Latitude},{gps.Longitude}");
}
// added to show authentication - does not exist in real MauiHttpRequestDecorator
if (someAuthService.TokenExpiry > DateTimeOffset.UtcNow)
await someAuthService.RefreshToken();
httpMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", someAuthService.AccessToken);
}
}

Generating All The Contracts and Responses

Some WebAPIs can have a large API surface with many routes, parameters, and types. Generating OpenAPI files is pretty standard these days with things like NSwag, Swashbuckle, & Refitter. Shiny Mediator can help with this as well. You can generate all the contracts and responses from an OpenAPI file or URI to an OpenAPI document.

Simply edit your csproj file that has Shiny.Mediator installed and add the following to it.

If there is no OperationId defined for the OpenAPI method, we cannot generate a contract.

<ItemGroup>
<MediatorHttp Include="OpenApiDoc.json" Namespace="My.Namespace" ContractPrefix="optional" ContractPostfix="HttpRequest" Visible="false" />
<MediatorHttp Include="OpenApiRemote" Uri="https://someurl.com" Namespace="My.RemoteNamespace" ContractPrefix="optional" ContractPostfix="HttpRequest" Visible="false" />
</ItemGroup>

:::note INFO

  • If you’re setting the Uri attribute, you must still set the Include value to satisfy the ItemGroup. Also note, the use of Visible=“false”. This removes the registration of the msbuild variable from the IDE solution explorer.
  • There are scenarios we don’t yet support like discrimated types. We’re looking at this for a future implementation. :::