feat(apiv2): basic error handling
This commit is contained in:
		
							
								
								
									
										51
									
								
								PluralKit.API/Errors.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								PluralKit.API/Errors.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
using Newtonsoft.Json.Linq;
 | 
			
		||||
 | 
			
		||||
namespace PluralKit.API
 | 
			
		||||
{
 | 
			
		||||
    public class PKError: Exception
 | 
			
		||||
    {
 | 
			
		||||
        public int ResponseCode { get; init; }
 | 
			
		||||
        public int JsonCode { get; init; }
 | 
			
		||||
        public PKError(int code, int json_code, string message) : base(message)
 | 
			
		||||
        {
 | 
			
		||||
            ResponseCode = code;
 | 
			
		||||
            JsonCode = json_code;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public JObject ToJson()
 | 
			
		||||
        {
 | 
			
		||||
            var j = new JObject();
 | 
			
		||||
            j.Add("message", this.Message);
 | 
			
		||||
            j.Add("code", this.JsonCode);
 | 
			
		||||
            return j;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class ModelParseError: PKError
 | 
			
		||||
    {
 | 
			
		||||
        public ModelParseError() : base(400, 0, "Error parsing JSON model")
 | 
			
		||||
        {
 | 
			
		||||
            // todo
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public new JObject ToJson()
 | 
			
		||||
        {
 | 
			
		||||
            var j = base.ToJson();
 | 
			
		||||
 | 
			
		||||
            return j;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class APIErrors
 | 
			
		||||
    {
 | 
			
		||||
        public static PKError GenericBadRequest = new(400, 0, "400: Bad Request");
 | 
			
		||||
        public static PKError SystemNotFound = new(404, 20001, "System not found.");
 | 
			
		||||
        public static PKError MemberNotFound = new(404, 20002, "Member not found.");
 | 
			
		||||
        public static PKError GroupNotFound = new(404, 20003, "Group not found.");
 | 
			
		||||
        public static PKError UnauthorizedMemberList = new(403, 30001, "Unauthorized to view member list");
 | 
			
		||||
        public static PKError UnauthorizedGroupList = new(403, 30002, "Unauthorized to view group list");
 | 
			
		||||
        public static PKError Unimplemented = new(501, 50001, "Unimplemented");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -7,7 +7,9 @@ using Autofac;
 | 
			
		||||
using Microsoft.AspNetCore.Authentication;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Builder;
 | 
			
		||||
using Microsoft.AspNetCore.Diagnostics;
 | 
			
		||||
using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc.Versioning;
 | 
			
		||||
using Microsoft.Extensions.Configuration;
 | 
			
		||||
@@ -15,6 +17,10 @@ using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Microsoft.Extensions.Hosting;
 | 
			
		||||
using Microsoft.OpenApi.Models;
 | 
			
		||||
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
using Serilog;
 | 
			
		||||
 | 
			
		||||
using PluralKit.Core;
 | 
			
		||||
 | 
			
		||||
namespace PluralKit.API
 | 
			
		||||
@@ -91,7 +97,7 @@ namespace PluralKit.API
 | 
			
		||||
            builder.RegisterInstance(InitUtils.BuildConfiguration(Environment.GetCommandLineArgs()).Build())
 | 
			
		||||
                .As<IConfiguration>();
 | 
			
		||||
            builder.RegisterModule(new ConfigModule<ApiConfig>("API"));
 | 
			
		||||
            builder.RegisterModule(new LoggingModule("api"));
 | 
			
		||||
            builder.RegisterModule(new LoggingModule("api", cfg: new LoggerConfiguration().Filter.ByExcluding(exc => exc.Exception is PKError)));
 | 
			
		||||
            builder.RegisterModule(new MetricsModule("API"));
 | 
			
		||||
            builder.RegisterModule<DataStoreModule>();
 | 
			
		||||
            builder.RegisterModule<APIModule>();
 | 
			
		||||
@@ -124,6 +130,23 @@ namespace PluralKit.API
 | 
			
		||||
                return next();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            app.UseExceptionHandler(handler => handler.Run(async ctx =>
 | 
			
		||||
            {
 | 
			
		||||
                var exc = ctx.Features.Get<IExceptionHandlerPathFeature>();
 | 
			
		||||
                if (exc.Error is not PKError)
 | 
			
		||||
                {
 | 
			
		||||
                    ctx.Response.StatusCode = 500;
 | 
			
		||||
                    await ctx.Response.WriteAsync("{\"message\":\"500: Internal Server Error\",\"code\":0}");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var err = (PKError)exc.Error;
 | 
			
		||||
                ctx.Response.StatusCode = err.ResponseCode;
 | 
			
		||||
 | 
			
		||||
                var json = JsonConvert.SerializeObject(err.ToJson());
 | 
			
		||||
                await ctx.Response.WriteAsync(json);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            app.UseMiddleware<AuthorizationTokenHandlerMiddleware>();
 | 
			
		||||
 | 
			
		||||
            //app.UseHttpsRedirection();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,11 +17,13 @@ namespace PluralKit.Core
 | 
			
		||||
    {
 | 
			
		||||
        private readonly string _component;
 | 
			
		||||
        private readonly Action<LoggerConfiguration> _fn;
 | 
			
		||||
        private LoggerConfiguration _cfg { get; init; }
 | 
			
		||||
 | 
			
		||||
        public LoggingModule(string component, Action<LoggerConfiguration> fn = null)
 | 
			
		||||
        public LoggingModule(string component, Action<LoggerConfiguration> fn = null, LoggerConfiguration cfg = null)
 | 
			
		||||
        {
 | 
			
		||||
            _component = component;
 | 
			
		||||
            _fn = fn ?? (_ => { });
 | 
			
		||||
            _cfg = cfg ?? new LoggerConfiguration();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Load(ContainerBuilder builder)
 | 
			
		||||
@@ -44,7 +46,7 @@ namespace PluralKit.Core
 | 
			
		||||
            var consoleTemplate = "[{Timestamp:HH:mm:ss.fff}] {Level:u3} {Message:lj}{NewLine}{Exception}";
 | 
			
		||||
            var outputTemplate = "[{Timestamp:yyyy-MM-dd HH:mm:ss.ffffff}] {Level:u3} {Message:lj}{NewLine}{Exception}";
 | 
			
		||||
 | 
			
		||||
            var logCfg = new LoggerConfiguration()
 | 
			
		||||
            var logCfg = _cfg
 | 
			
		||||
                .Enrich.FromLogContext()
 | 
			
		||||
                .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)
 | 
			
		||||
                .Enrich.WithProperty("Component", _component)
 | 
			
		||||
@@ -53,6 +55,9 @@ namespace PluralKit.Core
 | 
			
		||||
                // Don't want App.Metrics/D#+ spam
 | 
			
		||||
                .MinimumLevel.Override("App.Metrics", LogEventLevel.Information)
 | 
			
		||||
 | 
			
		||||
                // nor ASP.NET spam
 | 
			
		||||
                .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
 | 
			
		||||
 | 
			
		||||
                // Actual formatting for these is handled in ScalarFormatting
 | 
			
		||||
                .Destructure.AsScalar<SystemId>()
 | 
			
		||||
                .Destructure.AsScalar<MemberId>()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user