Configuration Comparison of Dependency Injection Containers (IOC)

Total: 12 Average: 4.3

I have often questioned myself which IoC container would be suitable for this or that project best. Their performance is only one side of the coin. The other side of the coin is the simplicity and speed of learning. So, I decided to compare the following containers from this point of view: Autofac, Simple Injector, StructureMap, Ninject, Unity, and Castle Windsor. In my opinion, these are the most popular IoC containers. You can find some of them in the list of the top 20 NuGet IoC packages. Also, I added a few containers based on my personal preferences. I really like Autofac and when writing this article I was reinforced in my choice in most of the cases.

In this article, I will describe the basics of the IoC containers, such as configuration and logging of components. I also want to compare the management of lifetime scope and advanced features. Code examples can be found in the LifetimeScopesExamples GitHub repository.

Read More

Replacement of Algorithm Testing with Testing of Effects Being Inserted

Total: 2 Average: 4.5

As I expected, Rule 8 from the article “Rules for Implementing TDD in Old Project” stating that we don’t need to test the algorithm of methods raised many “how” and “why” questions. When writing the previous article, it seemed obvious to me, so I did not go into much details on the matter. In this article, I provide a small sample code and two examples of how it could be tested.

Read More

Rules for Implementing TDD in Old Project

Total: 6 Average: 3.5

The article “Sliding Responsibility of the Repository Pattern” raised several questions, which are very difficult to answer. Do we need a repository if the complete disregard of technical details is impossible? How complex must the repository be so that its addition can be regarded worth-while? The answer to these questions varies depending on the emphasis placed in the development of systems. Probably the most difficult question is the following: do you even need a repository? The problem of “flowing abstraction” and the growing complexity of coding with an increase in the level of abstraction do not allow to find a solution that would satisfy both sides of the fence. For example, in reporting, intention design leads to the creation of a large number of methods for each filter and sorting, and a generic solution creates a large coding overhead.

Read More

Sliding Responsibility of the Repository Pattern

Total: 6 Average: 4.2

During numerous discussions about the applicability of the Repository pattern, I noticed that people are divided into two groups. In this article, I will call them abstractionists and concretists. The difference between them is in the way they treat the pattern value. The former believe that a repository is worthwhile, as it allows disregarding details of data storing. The latter suppose there is no way to disregard these details, therefore, the concept of repository makes no sense and its usage is a waste of time. The dispute between these two groups usually turns into a Holy War. Read More

Swashbuckle (Swagger) Customization for WebAPI

Total: 6 Average: 5

If you ever tested WebAPI, you are familiar with such tools as Postman or Advanced REST (the Chrome extensions). These solutions are convenient, but they have significant drawbacks too:

  • They can’t recognize which models the API accepts and which ones it returns.
  • They do not provide information about all possible endpoints.

The Swashbuckle package solves these disadvantages by building the Swagger specification generation and UI in the project.

This article will briefly describe how to bind Swashbuckle to your project and explain authorization and work with “overloaded” endpoints.

Binding to Project

Swashbuckle is the NuGet package that integrates auto-generation of information about nodes in WebAPI according to the OpenAPI specification. This specification is de facto a current standard, as WSDL once was.

To install Swashbuckle, do the following:

  1. Install it from NuGet using the Install-Package Swashbuckle command.
  2. Enable XML documentation in the project settings.
  3. Uncomment the IncludeXmlComments(GetXmlCommentsPath()); line in the SwaggerConfig.cs file – you’ll get it when you install the package.
  4. Write return string.Format(@”{0}\bin\BookStoreApiService.XML”, AppDomain.CurrentDomain.BaseDirectory); in the GetXmlCommentsPath() method implementation.

That is all for installing. Then, we need to describe the API methods, response codes, and customize them.

Nuances when deploying WebAPI

Deploying WebAPI into production may cause the issue with an XML file missing. The release build does not include such files by default. However, we can work it around.

The solution is editing the csproj file: add <ExcludeXmlAssemblyFiles>false</ExcludeXmlAssemblyFiles> in the project PropertyGroup. Then, the file will remain in bin/.

Another issue relates to those hiding API behind a proxy. Note that the solution is not universal. Still, it works for me. The proxy adds headers to the request, by which we know the endpoints URL for the client.

Example of URL recognition behind a proxy

// in the SwaggerConfig.cs file
c.RootUrl(req => ComputeClientHost(req));

// below you may find the implementation of the method
public static string ComputeClientHost(HttpRequestMessage req)
{
    var authority = req.RequestUri.Authority;
    var scheme = req.RequestUri.Scheme;
    // we receive the host, which the client can see
    if (req.Headers.Contains("X-Forwarded-Host"))
    {
        // if there is a proxy chain, it is necessary to take the first one
        var xForwardedHost = req.Headers.GetValues("X-Forwarded-Host").First();
        var firstForwardedHost = xForwardedHost.Split(',')[0];
        authority = firstForwardedHost;
    }
    // we get a protocol, which is used by the client 
    if (req.Headers.Contains("X-Forwarded-Proto"))
    {
        var xForwardedProto = req.Headers.GetValues("X-Forwarded-Proto").First();
        xForwardedProto = xForwardedProto.Split(',')[0];
        scheme = xForwardedProto;
    }	
    return scheme + "://" + authority;
}

Adding Response Codes

We can add the return HTTP Status Codes using XML comments and attributes.

Examples of adding status codes

/// <response code="404">Not Found</response>

[SwaggerResponse(HttpStatusCode.NotFound, Type = typeof(Model), Description = "Not Found: no such endpoint")]

We must remember that XML comments override attributes. If both are used simultaneously for the same method, it will ignore the attributes.

Besides, if we use XML comments, we must specify all the codes, including 200 (OK). The ‘return’ model cannot be specified.

Therefore, using SwaggerResponse is more preferable, as it does not have these drawbacks. The endpoint can return another code, for example, 201 (Created), instead of the default 200. In this case, the first one must be removed with the [SwaggerResponseRemoveDefaults] attribute.

There is an option to add standard codes, like 400 (BadRequest) or 401 (Unauthorized) to all methods at once. To do this, we must implement the IOperationFilter interface and register such a class using c.OperationFilter<T>();.

An example of using the Apply method to add a certain code list:

HttpStatusCode[] _codes; // codes for adding 
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
    // this property is now always initialized 
    if (operation.responses == null)
        operation.responses = new Dictionary<string, Response>();
    foreach (var code in _codes) {
        var codeNum = ((int)code).ToString();
        var codeName = code.ToString();
        // add description 
        if (!operation.responses.ContainsKey(codeNum))
            operation.responses.Add(codeNum, new Response { description = codeName });
    }
}

Authorization of WebAPI and Swashbuckle

There are several options for Basic authorization implementation. However, the package supports others too.

If AuthorizeAttribute is used, Swashbuckle will build UI, but queries will fail. There are several ways to fix this:

  1. Using authorization built into a browser
  2. Using a built-in authorization form in the package
  3. Using operation parameters
  4. Using JavaScript

Authorization built into the browser

Authorization built into a browser will be available “out of the box” if you use the attribute and filter:

// Basic Authorization attributes
config.Filters.Add(new AuthorizeAttribute());
config.Filters.Add(new BasicAuthenticationFilter()); // IauthenticationFilter implementation

After adding them to the WebAPI configuration, the browser prompts you to enter the authentication data when executing the query. The difficulty is, dropping this data is not as easy and fast as adding it.

Built-in authorization form in Swashbuckle

The other way is more comfortable as it provides a special form. To include the built-in authorization form in the package, do the following:

  1. Enable an attribute and filter for authentication.
  2. Uncomment the c.BasicAuth(“basic”).Description(“Basic HTTP Authentication”); line in the Swagger settings.
  3. Add IOperationFilter which adds the information in the c.OperationFilter<MarkSecuredMethodsOperationFilter>(); nodes.

Implementation of the Apply method of this filter

public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
    var filterPipeline = apiDescription.ActionDescriptor.GetFilterPipeline();
    // check if authorization is required
    var isAuthorized = filterPipeline
        .Select(filterInfo => filterInfo.Instance)
        .Any(filter => filter is IAuthorizationFilter);
    // check if anonymous access is allowed
    var allowAnonymous = apiDescription.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any();
    if (isAuthorized && !allowAnonymous)
    {
        if (operation.security == null)
            operation.security = new List<IDictionary<string, IEnumerable<string>>>();
        var auth = new Dictionary<string, IEnumerable<string>>
        {
            {"basic", Enumerable.Empty<string>()}
        };
        operation.security.Add(auth);
    }
}

After that, you will be able to use this authorization form. The data entered will be used for all queries.

Authorization form

Authorization using a parameter and JS code

The following two methods are examples of working with IOperationFilter and injecting your JavaScript.

Parameters can send data to not only the body and query but also to the header. Here, we must add a hash.

Adding such a parameter

operation.parameters.Add(new Parameter
{
    name = "Authorization",
    @in = "header", // specify that the value will be sent in the header
    description = "Basic U3dhZ2dlcjpUZXN0", // Basic Swagger:Test
    required = true, // parameter required
    type = "string"
});

By injecting your JavaScript, you can also send data in the header of queries. Do the following:

  1. Add a JS file as an embedded resource.
  2. Uncomment the line in the Swagger configuration and specify your file as a resource name: InjectJavaScript(thisAssembly, “assembly.namesapce.swagger-basic-auth.js”);
  3. In the file, write the following: swaggerUi.api.clientAuthorizations.add(“basic”, new SwaggerClient.ApiKeyAuthorization(“Authorization”, “Basic U3dhZ2dlcjpUZXN0”, “header”));

It will add the data as a header to each query. Actually, with this JS code, we can send any headers. The key parameter, which equals “basic” in the example, should be unique. Then we won’t receive a JS error when sending a query.

For example, JS sending headers to Swagger

swaggerUi.api.clientAuthorizations.add("custom1", new SwaggerClient.ApiKeyAuthorization("X-Header-1", "value1", "header"));
swaggerUi.api.clientAuthorizations.add("custom2", new SwaggerClient.ApiKeyAuthorization("X-Header-2", "value2", "header"));
swaggerUi.api.clientAuthorizations.add("custom3", new SwaggerClient.ApiKeyAuthorization("X-Header-3", "value3", "header"));

Working with mandatory headers

In some cases, unauthorized headers may be mandatory. For example, such are headers with the client’s information. Usually, a message handler is built into the WebAPI pipeline, the DelegatingHandler is implemented and registered in the WebAPI config.MessageHandlers.Add (new MandatoryHeadersHandler ()); configuration.

In this case, Swagger will stop showing any information, as the handler will not allow passing queries. There is no solution ‘out of the box.’ That’s why you must make provision for this case in your handler. In the case of a query to the swagger URL, the handler should pass it. Then adding headers with JS will help.

Endpoints with overloaded methods

WebAPI allows creating several action methods for one endpoint. The call of that endpoint depends on the query parameters.

[ResponseType(typeof (IList<Model>))]
public IHttpActionResult Get() {...}

[ResponseType(typeof (IList<Model>))]
public IHttpActionResult Get(int count, bool descending) {...}

Swagger does not support these methods by default. UI will throw the error message 500:

Not supported by Swagger 2.0: Multiple operations with path ‘api/<URL>’ and method ‘<METHOD>’. See the config setting — \«ResolveConflictingActions\» for a potential workaround.

Therefore, you should solve the situation yourself. There are several options:

  1. Select only one method.
  2. Create one method with all parameters.
  3. Change the document generation.

We implement the first and second options using the c.ResolveConflictingActions(Func<IEnumerable<ApiDescription>, ApiDescription> conflictingActionsResolver) settings.

The principle is taking several conflicting methods and returning one only.

An example of combining all the parameters

return apiDescriptions =>
{
    var descriptions = apiDescriptions as ApiDescription[] ?? apiDescriptions.ToArray();
    var first = descriptions.First(); // we build relative to the first method 
    var parameters = descriptions.SelectMany(d => d.ParameterDescriptions).ToList();

    first.ParameterDescriptions.Clear();
    // we add all the parameters and make them optional
    foreach (var parameter in parameters)
        if (first.ParameterDescriptions.All(x => x.Name != parameter.Name))
        {
            first.ParameterDescriptions.Add(new ApiParameterDescription
            {
                Documentation = parameter.Documentation,
                Name = parameter.Name,
                ParameterDescriptor = new OptionalHttpParameterDescriptor((ReflectedHttpParameterDescriptor) parameter.ParameterDescriptor),
                Source = parameter.Source
            });
        }
    return first;
};

// this inheritance is required, as IsOptional has only getter
public class OptionalHttpParameterDescriptor : ReflectedHttpParameterDescriptor
{
    public OptionalHttpParameterDescriptor(ReflectedHttpParameterDescriptor parameterDescriptor)
        : base(parameterDescriptor.ActionDescriptor, parameterDescriptor.ParameterInfo)
    {
    }
    public override bool IsOptional => true;
}

Drastic method

The third way is more drastic, originating from the OpenAPI specification. We can output all the endpoints with parameters:

Output of endpoints with parameters

To do this, we have to modify the way the Swagger documentation is generated using IDocumentFilter and generate the description ourselves.

In fact, we rarely need this method at work. So, we dig even deeper.

I would recommend another way only to those interested in Swashbuckle internals. It is to replace SwaggerGenerator.

We do it in the c.CustomProvider (defaultProvider => new NewSwaggerProvider (defaultProvider)); line:

  1. Create your MySwaggerGenerator: ISwaggerProvider class.
  2. Find SwaggerGenerator.cs in the Swashbuckle repository on GitHub (here).
  3. Copy the GetSwagger method and other related methods into yours.
  4. Duplicate internal variables and initialize them in the constructor of your class.
  5. Register in the Swagger configuration.

Initializing internal variables

Finally, you need to find ar paths = GetApiDescriptionsFor(apiVersion)….., a place where paths are created. For example, to get what is in the example, replace GroupBy() with .GroupBy(apiDesc => apiDesc.RelativePath).

Conclusion

Swashbuckle is a decent solution for WebAPI testing – it has all the advantages of Postman and Advanced REST, and provides more functionality. Among the rest, it recognized models accepted and returned by your API. Also, it informs the users about all possible endpoints.

Swashbuckle is easy to install, and it provides impressive functionality out of the box. We also shared some handy tips on its adjusting, configuring, and fixing the possible issues in the process. Altogether, this package is optimal.

References

  1. Swagger example
  2. RESTful Web API specification formats
  3. Customize Swashbuckle-generated API definitions
  4. Swagger object schema
  5. Authentication Filters in ASP.NET Web API 2
  6. A WebAPI Basic Authentication Authorization Filter
  7. Customize Authentication Header in SwaggerUI using Swashbuckle
  8. HTTP Message Handlers in ASP.NET Web API

DateTime to DateTimeOffset Transition

Total: 1 Average: 4

Imagine that you want to convert your system from one state to another. The initial state is when DateTime is used everywhere, both in C# code and in the database. The final state is when DateTimeOffset is used everywhere. You want to make the transition smooth and make as few changes as possible. This description can be the beginning of a very interesting problem with a dead end at the end.

Read More