Search loading...

API Hub

Explore and Make use of Nationally Defined Messaging APIs

 

Connection Code

Connection code Examples

Common NRLS Connection Code Examples

Connecting with C#

The following code samples are taken from the NRLS Demonstrator application which has both Consumer and Provider client implementations built in. More information about the design solution can be found on the NRLS Demonstrator Wiki

To start we create a base command request model that defines the type of request we want to make (GET, POST or DELETE) and then sets the required attributes. Within the model the full url to the NRLS API is constructed including appending any required parameter values passed in.

At this point we also generate our JWT using the ASID, ODS Code and User Role Profile ID values that are passed in.

Demonstrator/Demonstrator.NRLSAdapter/DocumentReferences/DocumentReferenceServices.cs#L91-L112 view raw
private CommandRequest BuildRequest(string asid, string resourceId, string nhsNumber, string custodianOrgCode, string typeCode, string jwtOrgCode, HttpMethod method, HttpContent content)
{
    var command = new CommandRequest
    {
        BaseUrl = $"{(_spineSettings.NrlsUseSecure ? _spineSettings.NrlsSecureServerUrl : _spineSettings.NrlsServerUrl)}",
        ResourceId = resourceId,
        ResourceType = ResourceType.DocumentReference,
        SearchParams = GetParams(nhsNumber, custodianOrgCode, resourceId, typeCode),
        Method = method,
        Content = content,
        UseSecure = _spineSettings.NrlsUseSecure,
        ClientThumbprint = ClientSettings(asid)?.Thumbprint,
        ServerThumbprint = _spineSettings.SpineThumbprint
    };

    var jwt = JwtFactory.Generate(method == HttpMethod.Get ? JwtScopes.Read : JwtScopes.Write, jwtOrgCode, "fakeRoleId", asid, command.FullUrl.AbsoluteUri, SystemUrlBase);

    command.Headers.Add(HeaderNames.Authorization, $"Bearer {jwt}");
    command.Headers.Add(FhirConstants.HeaderFromAsid, asid);
    command.Headers.Add(FhirConstants.HeaderToAsid, _spineSettings.SpineAsid);

    return command;


Once we have our command request model we call the FhirConnector service to start the actual call to the NRLS. We first build our HTTP message. At this point we also add in our NRLS specific headers that are held in our base request model and add in our DocumentReference model (http content) if we are performing a create (POST).

Demonstrator/Demonstrator.NRLSAdapter/Helpers/FhirConnector.cs#L115-L144 view raw
private HttpRequestMessage GetMessage(CommandRequest request)
{
    var httpRequest = new HttpRequestMessage()
    {
        RequestUri = new Uri(request.FullUrl.AbsoluteUri),
        Method = request.Method,
        Headers =
            {
                { HeaderNames.Accept, $"{ContentType.JSON_CONTENT_HEADER}"},
                { HeaderNames.AcceptEncoding, "gzip, deflate" },
                { HeaderNames.AcceptLanguage, "en-GB,en" },
                { HeaderNames.CacheControl, "no-cache" },
                { HeaderNames.Connection, "Keep-Alive" },
                { HeaderNames.Host, $"{request.FullUrl.Host}{(":" + request.FullUrl.Port ?? "")}" },
            }
    };

    //Add additional Spine Headers
    foreach (var header in request.Headers)
    {
        httpRequest.Headers.Add(header.Key, header.Value);
    }

    if (request.Content != null)
    {
        httpRequest.Content = request.Content;
    }

    return httpRequest;
}


Then we add in our certificate handling for mutual authentication:

Demonstrator/Demonstrator.NRLSAdapter/Helpers/FhirConnector.cs#L100-L111 view raw
{
    var handler = new HttpClientHandler();

    if (request.UseSecure)
    {
        handler.ClientCertificateOptions = ClientCertificateOption.Manual;
        handler.SslProtocols = SslProtocols.Tls12;
        handler.ClientCertificates.Add(ClientCertificate(request.ClientThumbprint));
        handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, errors) => ValidateServer(sender, cert, chain, errors, request.ServerThumbprint);
        handler.CheckCertificateRevocationList = false; //TODO: turn this on
    }


Next we make the call to the NRLS API and then parse the response. Here we are expecting either a FHIR Bundle or a FHIR OperationOutcome both of which inherit from a FHIR Resource. There is a check here to see if we have a success type HTTP response code. If not then we immediately raise an error.

Demonstrator/Demonstrator.NRLSAdapter/Helpers/FhirConnector.cs#L48-L92 view raw
using (var client = new HttpClient(handler))
{
    var httpRequest = GetMessage(request);

    using (HttpResponseMessage res = await client.SendAsync(httpRequest))
    using (HttpContent content = res.Content)
    {
        //res.EnsureSuccessStatusCode(); //will throw a HttpRequestException to catch in future

        var data = content.ReadAsStreamAsync().Result;

        if (data == null)
        {
            throw new HttpRequestException($"Request resulted in nothing for: {request.FullUrl}.");
        }

        if(res.Headers?.Location != null)
        {
            fhirResponse.ResponseLocation = res.Headers.Location;
        }

        using (var reader = new StreamReader(data, Encoding.UTF8))
        {
            try
            {
                var body = reader.ReadToEnd();

                if(!string.IsNullOrEmpty(body))
                {
                    var jsonParser = new FhirJsonParser();
                    fhirResponse.Resource = jsonParser.Parse<Resource>(body);
                }

                if (!res.IsSuccessStatusCode)
                {
                    throw new HttpRequestException(new FhirJsonSerializer().SerializeToString(fhirResponse.GetResource<OperationOutcome>()));
                }
            }
            catch (Exception ex)
            {
                throw new HttpRequestException(ex.Message, ex.InnerException);
            }
        }
    }

All content is available under the Open Government Licence v3.0, except where otherwise stated