CSharp Rest API 2

From bibbleWiki
Jump to navigation Jump to search

Introduction

So why two pages. I needed to revisit this and put my thoughts down as things happened

Setup

dotnet SDK

This first thing to note is the installing the dotnet SDK is best done via apt. I could not get this to work using snap

Project Structure

Overview

My plan is to create three projects, Api, Domain and Data in an attempt to try an be more clean about my approach.

Directory.Build.props

To achieve this I needed to make a Directory.Build.props file with the command.

dotnet new buildprops --use-artifacts

Obviously the defaults were rubbish and you end up thinking why not just type the file. Why indeed.

<Project>
  <PropertyGroup>
    <ArtifactsPath>$(MSBuildThisFileDirectory)Build</ArtifactsPath>
  </PropertyGroup>
</Project>

build.proj

Don't know why I needed to do this but here it is.

<Project Sdk="Microsoft.Build.Traversal/3.0.0"><PropertyGroup>  <UserSecretsId>cfe2b025-33ab-4dc2-a72a-0e79c417ce17</UserSecretsId>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="**/*.*proj" />
  </ItemGroup>
</Project>

launch.json

Now the Launch file. Needed to comment out the section to stop the browser opening up each time. Why would you do this.

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": ".NET Core Launch (web)",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build",
            "program": "${workspaceFolder}/Build/bin/Api/debug/Api.dll",
            "args": [],
            "cwd": "${workspaceFolder}/Api",
            "stopAtEntry": false,
            // "serverReadyAction": {
            //     "action": "openExternally",
            //     "pattern": "\\bNow listening on:\\s+(https?://\\S+)"
            // },
            "env": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            },
            "sourceFileMap": {
                "/Views": "${workspaceFolder}/Views"
            },
        },
    ]
}

Next I need to make a launch.json

Configuration

Currently rolling with the appssetttings.json detailed on CSharp Rest API

Health Checks

I am using postgres for this to see the impact and to have and understanding of the options. For the heath checks they provide AspNetCore.HealthChecks.NpgSql and this is easy to set up. I also noticed there is a UI for health checks in AspNetCore.HealthChecks.UI.Client. This made the thing possibly more useful (maybe need to thing about security). But there it is

// Add the endpoint /health
app.MapHealthChecks("/health", new HealthCheckOptions()
{
    Predicate = _ => true,
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

And when the postgres is implemented we get

{
  "status": "Unhealthy",
  "totalDuration": "00:00:00.1706495",
  "entries": {
     "postgresql": {
       "data": {},
       "description": "Failed to connect to 127.0.0.1:5432",
       "duration": "00:00:00.1124122",
       "exception": "Failed to connect to 127.0.0.1:5432",
       "status": "Unhealthy",
       "tags": []
      }
   }
}

API Versioning

Reading the book Coding clean they provide the following approach with look really simple. First install Asp.Versioning.Http

dotnet add package  Asp.Versioning.Http

And add the following

builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.ReportApiVersions = true;
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ApiVersionReader = new HeaderApiVersionReader("api-version");
});

Now we can do this to have two versions

var versionSet = app.NewApiVersionSet()
                    .HasApiVersion(1.0)
                    .HasApiVersion(2.0)
                    .Build();

And we could then have

app.MapGet("/version", () => "Hello version 1").WithApiVersionSet(versionSet).MapToApiVersion(1.0);
app.MapGet("/version", () => "Hello version 2").WithApiVersionSet(versionSet).MapToApiVersion(2.0);

Now we just need to put the api-version in as a header, if we don't want the default of version 1.

Making Swagger Play Ball With Api Versioning

This did not work once I added the versioning. But this from the Coding Clean book by Anthony Giretti worked. There is also help on stackoverflow]
So I did this,

Installed ApiExplorer

dotnet add package Asp.Versioning.Mvc.ApiExplorer

Created a SwaggerGenOptions implementation

Here was the code from the book

public class SwaggerConfigurationsOptions : IConfigureOptions<SwaggerGenOptions>
{
    private readonly IApiVersionDescriptionProvider _apiVersionDescriptionProvider;
    public SwaggerConfigurationsOptions(IApiVersionDescriptionProvider apiVersionDescriptionProvider)
    {
        _apiVersionDescriptionProvider = apiVersionDescriptionProvider;
    }

    public void Configure(SwaggerGenOptions options)
    {
        foreach (var description in _apiVersionDescriptionProvider.ApiVersionDescriptions)
        {
            options.SwaggerDoc(description.GroupName, CreateOpenApiInfo(description));
        }
    }
    private static OpenApiInfo CreateOpenApiInfo(ApiVersionDescription description)
    {
        var info = new OpenApiInfo()
        {
            Title = "C# Rest API Example",
            Version = description.ApiVersion.ToString()
        };
        return info;
    }
}

Used it in my services creation

Amended Versioning and added the implementation for SwaggerGenOptions

builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.ReportApiVersions = true;
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ApiVersionReader = new HeaderApiVersionReader("api-version");
})
.AddApiExplorer(options =>
{
    options.GroupNameFormat = "'v'VV"; // Formats the version as follow: "'v'major[.minor]"
});

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<IConfigureOptions<SwaggerGenOptions>, SwaggerConfigurationsOptions>();

Finally Added the Versions, hard coded.

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1.0/swagger.json", "Api Version 1.0");
        c.SwaggerEndpoint("/swagger/v2.0/swagger.json", "Api Version 2.0");
    });
}

Now you get the dropbox with the version.