Skip to content

Commit

Permalink
added ping controller
Browse files Browse the repository at this point in the history
  • Loading branch information
lkurzyniec committed May 7, 2024
1 parent 11698ad commit c3667a1
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 194 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -95,7 +95,7 @@ At the end, You are in charge, so it's your decision to which path you would lik
* Configurations
* `Serilog` configuration place - [SerilogConfigurator.cs](src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Configurations/SerilogConfigurator.cs)
* `Swagger` registration place - [SwaggerRegistration.cs](src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Registrations/SwaggerRegistration.cs)
* Simple exemplary API controllers - [EmployeesController.cs](src/HappyCode.NetCoreBoilerplate.Api/Controllers/EmployeesController.cs), [CarsController.cs](src/HappyCode.NetCoreBoilerplate.Api/Controllers/CarsController.cs)
* Simple exemplary API controllers - [EmployeesController.cs](src/HappyCode.NetCoreBoilerplate.Api/Controllers/EmployeesController.cs), [CarsController.cs](src/HappyCode.NetCoreBoilerplate.Api/Controllers/CarsController.cs), [PingsController.cs](src/HappyCode.NetCoreBoilerplate.Api/Controllers/PingsController.cs)
* Example of BackgroundService - [PingWebsiteBackgroundService.cs](src/HappyCode.NetCoreBoilerplate.Api/BackgroundServices/PingWebsiteBackgroundService.cs)

![HappyCode.NetCoreBoilerplate.Api](.assets/api.png "HappyCode.NetCoreBoilerplate.Api")
Expand Down
@@ -1,48 +1,58 @@
using System.Net.Http;
using HappyCode.NetCoreBoilerplate.Core.Settings;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace HappyCode.NetCoreBoilerplate.Api.BackgroundServices
{
public class PingWebsiteBackgroundService : BackgroundService
{
private readonly PeriodicTimer _timer;
private readonly HttpClient _client;
private readonly ILogger<PingWebsiteBackgroundService> _logger;
private readonly IOptions<PingWebsiteSettings> _configuration;

public PingWebsiteBackgroundService(
IHttpClientFactory httpClientFactory,
ILogger<PingWebsiteBackgroundService> logger,
IOptions<PingWebsiteSettings> configuration)
{
_client = httpClientFactory.CreateClient(nameof(PingWebsiteBackgroundService));
_logger = logger;
_configuration = configuration;
_timer = new PeriodicTimer(TimeSpan.FromMinutes(_configuration.Value.TimeIntervalInMinutes));
}

protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
_logger.LogInformation("{BackgroundService} running at '{Date}', pinging '{URL}'",
nameof(PingWebsiteBackgroundService), DateTime.Now, _configuration.Value.Url);
try
{
using var response = await _client.GetAsync(_configuration.Value.Url, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
_logger.LogInformation("Is '{Host}' responding: {Status}",
_configuration.Value.Url.Authority, response.IsSuccessStatusCode);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Error during ping");
}
await _timer.WaitForNextTickAsync(cancellationToken);
}
_timer.Dispose();
}
}
}
using System.Net;
using System.Net.Http;
using HappyCode.NetCoreBoilerplate.Core.Settings;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace HappyCode.NetCoreBoilerplate.Api.BackgroundServices
{
public interface IPingService
{
public HttpStatusCode WebsiteStatusCode { get; }
}

public class PingWebsiteBackgroundService : BackgroundService, IPingService
{
private readonly PeriodicTimer _timer;
private readonly HttpClient _client;
private readonly ILogger<PingWebsiteBackgroundService> _logger;
private readonly IOptions<PingWebsiteSettings> _configuration;

public HttpStatusCode WebsiteStatusCode { get; private set; }

public PingWebsiteBackgroundService(
IHttpClientFactory httpClientFactory,
ILogger<PingWebsiteBackgroundService> logger,
IOptions<PingWebsiteSettings> configuration)
{
_client = httpClientFactory.CreateClient(nameof(PingWebsiteBackgroundService));
_logger = logger;
_configuration = configuration;

_timer = new PeriodicTimer(TimeSpan.FromMinutes(_configuration.Value.TimeIntervalInMinutes));
}

protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
_logger.LogInformation("{BackgroundService} running at '{Date}', pinging '{URL}'",
nameof(PingWebsiteBackgroundService), DateTime.Now, _configuration.Value.Url);
try
{
using var response = await _client.GetAsync(_configuration.Value.Url, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
WebsiteStatusCode = response.StatusCode;
_logger.LogInformation("Is '{Host}' responding: {Status}",
_configuration.Value.Url.Authority, response.IsSuccessStatusCode);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Error during ping");
}
await _timer.WaitForNextTickAsync(cancellationToken);
}
_timer.Dispose();
}
}
}
@@ -0,0 +1,41 @@
using System.Net;
using HappyCode.NetCoreBoilerplate.Api.BackgroundServices;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace HappyCode.NetCoreBoilerplate.Api.Controllers
{
[AllowAnonymous]
[Route("api/pings")]
public class PingsController : ApiControllerBase
{
private readonly IPingService _pingService;

public PingsController(IPingService pingService)
{
_pingService = pingService;
}

[HttpGet("website")]
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
public IActionResult GetWebsitePingStatusCode()
{
var result = _pingService.WebsiteStatusCode;
return Ok($"{(int)result} ({result})");
}

[HttpGet("random")]
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
public IActionResult GetRandomStatusCode()
{
var random = new Random(Guid.NewGuid().GetHashCode());
int pretender;
do
{
pretender = random.Next(100, 600);
} while (!Enum.IsDefined(typeof(HttpStatusCode), pretender));
return Ok($"{pretender} ({(HttpStatusCode)pretender})");
}
}
}
200 changes: 101 additions & 99 deletions src/HappyCode.NetCoreBoilerplate.Api/Startup.cs
@@ -1,99 +1,101 @@
using HappyCode.NetCoreBoilerplate.Api.BackgroundServices;
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Configurations;
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters;
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Registrations;
using HappyCode.NetCoreBoilerplate.BooksModule;
using HappyCode.NetCoreBoilerplate.Core;
using HappyCode.NetCoreBoilerplate.Core.Registrations;
using HappyCode.NetCoreBoilerplate.Core.Settings;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.FeatureManagement;
using Microsoft.FeatureManagement.FeatureFilters;
using Swashbuckle.AspNetCore.SwaggerUI;

namespace HappyCode.NetCoreBoilerplate.Api
{
public class Startup
{
private readonly IConfiguration _configuration;

public Startup(IConfiguration configuration)
{
_configuration = configuration;
}

public virtual void ConfigureServices(IServiceCollection services)
{
services
.AddHttpContextAccessor()
.AddRouting(options => options.LowercaseUrls = true);

services.AddMvcCore(options =>
{
options.Filters.Add<HttpGlobalExceptionFilter>();
options.Filters.Add<ValidateModelStateFilter>();
options.Filters.Add<ApiKeyAuthorizationFilter>();
})
.AddApiExplorer()
.AddDataAnnotations();

//there is a difference between AddDbContext() and AddDbContextPool(), more info https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.0#dbcontext-pooling and https://stackoverflow.com/questions/48443567/adddbcontext-or-adddbcontextpool
services.AddDbContext<EmployeesContext>(options => options.UseMySql(_configuration.GetConnectionString("MySqlDb"), ServerVersion.Parse("8.0")), contextLifetime: ServiceLifetime.Transient, optionsLifetime: ServiceLifetime.Singleton);
services.AddDbContextPool<CarsContext>(options => options.UseSqlServer(_configuration.GetConnectionString("MsSqlDb")), poolSize: 10);

services.Configure<ApiKeySettings>(_configuration.GetSection("ApiKey"));
services.AddSwagger(_configuration);

services.Configure<PingWebsiteSettings>(_configuration.GetSection("PingWebsite"));
services.AddHostedService<PingWebsiteBackgroundService>();
services.AddHttpClient(nameof(PingWebsiteBackgroundService));

services.AddCoreComponents();
services.AddBooksModule(_configuration);

services.AddFeatureManagement()
.AddFeatureFilter<TimeWindowFilter>();

services.AddHealthChecks()
.AddMySql(_configuration.GetConnectionString("MySqlDb"))
.AddSqlServer(_configuration.GetConnectionString("MsSqlDb"))
.AddBooksModule(_configuration);
}

public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBooksModule();
endpoints.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse,
});
});

app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Simple Api V1");
c.DocExpansion(DocExpansion.None);
});

app.InitBooksModule();
}
}
}
using System.Linq;
using HappyCode.NetCoreBoilerplate.Api.BackgroundServices;
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Configurations;
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters;
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Registrations;
using HappyCode.NetCoreBoilerplate.BooksModule;
using HappyCode.NetCoreBoilerplate.Core;
using HappyCode.NetCoreBoilerplate.Core.Registrations;
using HappyCode.NetCoreBoilerplate.Core.Settings;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.FeatureManagement;
using Microsoft.FeatureManagement.FeatureFilters;
using Swashbuckle.AspNetCore.SwaggerUI;

namespace HappyCode.NetCoreBoilerplate.Api
{
public class Startup
{
private readonly IConfiguration _configuration;

public Startup(IConfiguration configuration)
{
_configuration = configuration;
}

public virtual void ConfigureServices(IServiceCollection services)
{
services
.AddHttpContextAccessor()
.AddRouting(options => options.LowercaseUrls = true);

services.AddMvcCore(options =>
{
options.Filters.Add<HttpGlobalExceptionFilter>();
options.Filters.Add<ValidateModelStateFilter>();
options.Filters.Add<ApiKeyAuthorizationFilter>();
})
.AddApiExplorer()
.AddDataAnnotations();

//there is a difference between AddDbContext() and AddDbContextPool(), more info https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.0#dbcontext-pooling and https://stackoverflow.com/questions/48443567/adddbcontext-or-adddbcontextpool
services.AddDbContext<EmployeesContext>(options => options.UseMySql(_configuration.GetConnectionString("MySqlDb"), ServerVersion.Parse("8.0")), contextLifetime: ServiceLifetime.Transient, optionsLifetime: ServiceLifetime.Singleton);
services.AddDbContextPool<CarsContext>(options => options.UseSqlServer(_configuration.GetConnectionString("MsSqlDb")), poolSize: 10);

services.Configure<ApiKeySettings>(_configuration.GetSection("ApiKey"));
services.AddSwagger(_configuration);

services.Configure<PingWebsiteSettings>(_configuration.GetSection("PingWebsite"));
services.AddHttpClient(nameof(PingWebsiteBackgroundService));
services.AddHostedService<PingWebsiteBackgroundService>();
services.AddSingleton(x => x.GetServices<IHostedService>().OfType<IPingService>().Single());

services.AddCoreComponents();
services.AddBooksModule(_configuration);

services.AddFeatureManagement()
.AddFeatureFilter<TimeWindowFilter>();

services.AddHealthChecks()
.AddMySql(_configuration.GetConnectionString("MySqlDb"))
.AddSqlServer(_configuration.GetConnectionString("MsSqlDb"))
.AddBooksModule(_configuration);
}

public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBooksModule();
endpoints.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse,
});
});

app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Simple Api V1");
c.DocExpansion(DocExpansion.None);
});

app.InitBooksModule();
}
}
}

0 comments on commit c3667a1

Please sign in to comment.