using Fantasy; using Fantasy.Async; using Fantasy.Event; using Fantasy.Network.HTTP; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; namespace NBF; public class GameHttpServicesHandler : AsyncEventSystem { protected override async FTask Handler(OnConfigureHttpServices self) { self.MvcBuilder.AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.WriteIndented = true; options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); }); self.MvcBuilder.AddMvcOptions(options => { // options.Filters.Add(); // options.Filters.Add(); }); self.MvcBuilder.ConfigureApiBehaviorOptions(options => { options.InvalidModelStateResponseFactory = context => { var errorText = string.Join("; ", context.ModelState .Where(kv => kv.Value is { Errors.Count: > 0 }) .Select(kv => $"{kv.Key}: {string.Join(", ", kv.Value!.Errors.Select(e => e.ErrorMessage))}")); var response = new ResponseData { Code = (int)ErrorCode.ArgsError, Data = string.IsNullOrWhiteSpace(errorText) ? "args error" : errorText }; // Keep API error style consistent with NBControllerBase.Error(): HTTP 200 + business code. return new OkObjectResult(response); }; }); self.Builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = ApiJwtHelper.Issuer, ValidAudience = ApiJwtHelper.Audience, IssuerSigningKey = ApiJwtHelper.CreateSigningKey(), ClockSkew = TimeSpan.Zero }; }); self.Builder.Services.AddAuthorization(options => { options.AddPolicy("Player", policy => policy.RequireRole("Player", "Admin")); options.AddPolicy("Admin", policy => policy.RequireRole("Admin")); options.AddPolicy("VIP", policy => policy.RequireClaim("VIPLevel")); }); self.Builder.Services.AddCors(options => { options.AddPolicy("GameClient", builder => { builder.WithOrigins( "https://game.example.com", "https://cdn.game.example.com" ) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); }); }); Log.Info($"[HTTP] game service configured: Scene {self.Scene.SceneConfigId}"); await FTask.CompletedTask; } }