Sphyrnidae Common Library  2.0.1
Shared Utilities/Library
API Methods

Pipeline / Middleware

The Sphyrnidae.Common.Api namespace is mostly about building a robust pipeline for a modern WebAPI. This namespace is driven by the ServiceConfiguration class. It comes pre-configured to do all of the following things:

The default pipeline used in the configuration is:

  1. DevelopmentHsts - Dev/Localhost: app.UseDeveloperExceptionPage(); Other: app.UseHsts();
  2. HttpsRedirect - app.UseHttpsRedirection();
  3. Swagger - Enable swagger/UI (app.UseSwagger(); app.UseSwaggerUI();)
  4. Routing - app.UseRouting();
  5. Cors - app.UseCors(config.CorsPolicyName);
  6. HealthCheck - app.UseHealthChecks(config.HealthCheckEndpoint, config.HealthCheckOptions(envSettings));
  7. HttpData
  8. Authentication
  9. JWT
  10. Exceptions
  11. Logging
  12. Controller API Call - app.UseEndpoints(endpoints => {endpoints.MapControllers();});

You can create/add any additional middleware components you want to this pipeline, or totally redo the pipeline.

Service Registration

In your own application, you need only worry about things that are specific to your application. However, you do need to wire in all of this configuration into your startup.cs class. Information on doing this is given in API Setup.

Attributes

There are a number of Attributes. There are 2 types of attributes:

  1. Validation Attributes:
    1. EnumValidator
    2. NotDefault
    3. RegEx
  2. Pipeline configuration attributes:
    1. Authentication
    2. SkipLog

Authentication

The Authentication middleware component take care of Authentication and Authorization checks. If your API endpoint needs to be secured, you should put the Authentication attribute on it. This attribute has one of the following types:

  1. None: Use this type if you have specified the entire controller should be secured, but not this particular endpoint. If an authenticated user accesses this endpoint, that will be allowed. If an unauthenticated user access this endpoint, they will be assigned the default/public identity: see GetDefaultIdentity() method
  2. Jwt: This is the primary mechanism for authentication. To learn more about JWT's, go here
  3. ApiToApi: Microservice calls will provide their authorization "key" to the next service. Using this type ensures that this endpoint is hit only by a validated microservice call.

The JWT is basically a serialized and encrypted Identity Object - generated and decrypted using the IIdentityHelper.

The JWT contains a collection of roles (List<string>) that the user has access to. By specifying a named role in the Authentication attribute, this will first Authenticate via the JWT, and then Authorize based on the user having that named role in their role collection.

The JWT middleware component does not play a direct role in Authentication/Authorization. What this component does instead is places a refreshed JWT into the Authorization header of the HTTP Response. If there were any updates to the Identity Object, you will need to update the Current object with this updated/new identity.

For more information on Authentication and Authorization, go here

Responses

In your business logic, you may need to return a non-2xx response. If you are in the controller, you have access to methods such as Ok() or NotFound(). However, in other layers, you don't have access to these methods. Instead, you should be returning a business entity and relying on the controller to actually convert that entity into a response object. I have created a class called ApiResponseStandard which serves that purpose.

ApiResponseStandard is the business entity that contains all the information needed for a controller to convert this to an HTTP response. To create an ApiResponseStandard, you can use the static methods contained in ApiResponse.

When this object is returned to the controller, you can execute one of the base class methods. The BaseApi class takes as a constructor argument IApiResponse. This class is injected, and by calling the methods FormatResponse() or GetWithDefaultsRemoved(), this will create the proper response from the ApiResponseStandard object. By default, the registered implementation of IApiResponse is ApiResponseStandard. If you'd like to format the HTTP response differently, you can register a new implementation. The common scenario I've seen for this is when you're trying to differentiate between a network 404 and a server/application 404.

Additionally, if you have a customized HTTP response object (eg. not just the simple return object in the body of the response), you will need to update ModelState validation. This will be done by registering a new ApiControllerConfiguration. Your custom implementation will be registered with the builder as builder.ConfigureApiBehaviorOptions(YOUR_METHOD), and to override the ModelState validation error, you should specify a custom implementation for InvalidModelStateResponseFactory.

Error Handling

The Exceptions middleware component will trap and log all exceptions that occur during the API call. There are 2 types of exceptions that will be handled slightly differently:

  1. UserException will record the exception, and send the full message back in the HTTP response
  2. All others will still record the exception, but will instead send back in the HTTP response a "nice" message: We're sorry, but something went wrong with your request. The details of this issue have been captured - please reference issue #{guid} if you need to contact the help desk.

Logging

The Logging middleware will ensure that all API calls are logged. The logging of these calls will contain both request and response information (eg. the response body and how long it took).

Additionally, the custom sphyrnidae middleware componets ( HttpData, Authentication, JWT, Exceptions, Logging) will all log that the middleware component is executing (starting, and stopping with time elapsed).