Files
OptixServe/OptixServe.Api/Program.cs
Huxley Deng 6fd6c9f20d Enable API versioning with route group.
Fix: the API routing is now versioned with prefix `api/v1`, aligned to OpenAPI specifications.
2025-07-07 16:16:58 +08:00

122 lines
4.2 KiB
C#

using System.CommandLine;
using OptixServe.Api.Endpoints;
using OptixServe.Core.Services;
class Program
{
/// <summary>
/// Main method that configures and runs the application
/// </summary>
/// <param name="args">Command line arguments</param>
/// <returns>A Task representing the asynchronous operation</returns>
static async Task Main(string[] args)
{
var rootCommand = new RootCommand("OptixServe API");
// Configure the --config/-c command line option
var configOption = new Option<FileInfo?>("--config", "-c")
{
Description = "Config file path",
CustomParser = arg =>
{
if (!arg.Tokens.Any()) return null;
var filePath = arg.Tokens.Single().Value;
if (!File.Exists(filePath))
{
arg.AddError($"Configuration file not found: {filePath}");
return null;
}
return new FileInfo(filePath);
}
};
rootCommand.Add(configOption);
// Set handle for rootCommand
// The handle prototype and way of parsing arguments
// is changed in System.CommandLine 2.0.0-beta5
rootCommand.SetAction((ParseResult parseResult) =>
{
var builder = WebApplication.CreateSlimBuilder(args);
var configFile = parseResult.GetValue(configOption);
builder.AddConfigurationWithCommand(configFile);
builder.RegisterServices();
builder.RegiserJsonContext();
var app = builder.Build();
var apiGroup = app.MapGroup("api/v1");
ExtensionMethods.RegisterEndpoints(apiGroup);
app.Run();
});
// Parse arguments then invoke the command
var parseResult = rootCommand.Parse(args);
await parseResult.InvokeAsync();
}
}
/// <summary>
/// Contains extension methods for WebApplicationBuilder and WebApplication
/// </summary>
static class ExtensionMethods
{
/// <summary>
/// Adds configuration sources to the application builder
/// </summary>
/// <param name="builder">WebApplicationBuilder instance</param>
/// <param name="file">Optional configuration file specified via command line</param>
public static void AddConfigurationWithCommand(this WebApplicationBuilder builder, FileInfo? file)
{
// Configure configuration sources in specified order
var configurationBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true)
.AddJsonFile("config.json", optional: true)
.AddJsonFile($"config.{builder.Environment.EnvironmentName}.json", optional: true);
// Add command-line specified config file if provided
if (file != null)
{
configurationBuilder.AddJsonFile(file.FullName, optional: false);
}
builder.Configuration.AddConfiguration(configurationBuilder.Build());
}
/// <summary>
/// Configures JSON serialization options with custom context
/// </summary>
/// <param name="builder">WebApplicationBuilder instance</param>
public static void RegiserJsonContext(this WebApplicationBuilder builder)
{
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.TypeInfoResolverChain.Add(UserJsonContext.Default);
});
}
/// <summary>
/// Configures services for DI
/// </summary>
/// <param name="builder"></param>
public static void RegisterServices(this WebApplicationBuilder builder)
{
// Application services
builder.Services.AddScoped<IUserService, UserService>();
}
/// <summary>
/// Registers all API endpoints
/// </summary>
/// <param name="app">WebApplication instance</param>
public static void RegisterEndpoints(RouteGroupBuilder rootGroup)
{
UserEndpoint.Register(rootGroup);
}
}