Start implementing card
This commit is contained in:
parent
f7761f200f
commit
9c759c3b1a
@ -11,11 +11,14 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ChoETL" Version="1.2.1.52" />
|
||||||
<PackageReference Include="MediatR" Version="11.1.0" />
|
<PackageReference Include="MediatR" Version="11.1.0" />
|
||||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
|
||||||
<PackageReference Include="Riok.Mapperly" Version="2.7.0-next.2" />
|
<PackageReference Include="Riok.Mapperly" Version="2.7.0-next.2" />
|
||||||
|
<PackageReference Include="Throw" Version="1.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
24
Application/Common/Behaviours/LoggingBehaviour.cs
Normal file
24
Application/Common/Behaviours/LoggingBehaviour.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using MediatR.Pipeline;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Application.Common.Behaviours;
|
||||||
|
|
||||||
|
public class LoggingBehaviour<TRequest> : IRequestPreProcessor<TRequest> where TRequest : notnull
|
||||||
|
{
|
||||||
|
private readonly ILogger<TRequest> logger;
|
||||||
|
|
||||||
|
// ReSharper disable once ContextualLoggerProblem
|
||||||
|
public LoggingBehaviour(ILogger<TRequest> logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Process(TRequest request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var requestName = typeof(TRequest).Name;
|
||||||
|
|
||||||
|
logger.LogInformation("Received request: {RequestName}, content: {Request}", requestName, request);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
33
Application/Common/Behaviours/UnhandledExceptionBehaviour.cs
Normal file
33
Application/Common/Behaviours/UnhandledExceptionBehaviour.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Application.Common.Behaviours;
|
||||||
|
|
||||||
|
public class UnhandledExceptionBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
|
||||||
|
where TRequest : IRequest<TResponse>
|
||||||
|
{
|
||||||
|
private readonly ILogger<TRequest> logger;
|
||||||
|
|
||||||
|
// ReSharper disable once ContextualLoggerProblem
|
||||||
|
public UnhandledExceptionBehaviour(ILogger<TRequest> logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await next();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var requestName = typeof(TRequest).Name;
|
||||||
|
|
||||||
|
logger.LogError(ex, "Unhandled Exception for Request {Name} {@Request}", requestName, request);
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
8
Application/Common/Exceptions/CardExistsException.cs
Normal file
8
Application/Common/Exceptions/CardExistsException.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Application.Common.Exceptions;
|
||||||
|
|
||||||
|
public class CardExistsException : Exception
|
||||||
|
{
|
||||||
|
public CardExistsException(string? message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
17
Application/Common/Extensions/XmlSerializationExtensions.cs
Normal file
17
Application/Common/Extensions/XmlSerializationExtensions.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using ChoETL;
|
||||||
|
using Throw;
|
||||||
|
|
||||||
|
namespace Application.Common;
|
||||||
|
|
||||||
|
public static class XmlSerializationExtensions
|
||||||
|
{
|
||||||
|
public static T DeserializeCardData<T>(this string source) where T : class
|
||||||
|
{
|
||||||
|
using var reader = new ChoXmlReader<T>(new StringReader(source)).WithXPath("/root/data");
|
||||||
|
|
||||||
|
var result = reader.Read();
|
||||||
|
result.ThrowIfNull();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
140
Application/Common/Models/ServiceError.cs
Normal file
140
Application/Common/Models/ServiceError.cs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
namespace Application.Common.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All errors contained in ServiceResult objects must return an error of this type
|
||||||
|
/// Error codes allow the caller to easily identify the received error and take action.
|
||||||
|
/// Error messages allow the caller to easily show error messages to the end user.
|
||||||
|
///
|
||||||
|
/// Taken from https://github.com/iayti/CleanArchitecture/blob/master/src/Common/CleanArchitecture.Application/Common/Models/ServiceError.cs
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class ServiceError
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CTOR
|
||||||
|
/// </summary>
|
||||||
|
public ServiceError(string message, int code)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceError()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Human readable error message
|
||||||
|
/// </summary>
|
||||||
|
public string Message { get; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Machine readable error code
|
||||||
|
/// </summary>
|
||||||
|
public int Code { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default error for when we receive an exception
|
||||||
|
/// </summary>
|
||||||
|
public static ServiceError DefaultError => new("An unknown exception occured.", 999);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default validation error. Use this for invalid parameters in controller actions and service methods.
|
||||||
|
/// </summary>
|
||||||
|
public static ServiceError ModelStateError(string validationError)
|
||||||
|
{
|
||||||
|
return new ServiceError(validationError, 998);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use this for unauthorized responses.
|
||||||
|
/// </summary>
|
||||||
|
public static ServiceError ForbiddenError => new("You are not authorized to call this action.", 998);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use this to send a custom error message
|
||||||
|
/// </summary>
|
||||||
|
public static ServiceError CustomMessage(string errorMessage)
|
||||||
|
{
|
||||||
|
return new ServiceError(errorMessage, 997);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServiceError UserNotFound => new("User with this id does not exist", 996);
|
||||||
|
|
||||||
|
public static ServiceError UserFailedToCreate => new("Failed to create User.", 995);
|
||||||
|
|
||||||
|
public static ServiceError Canceled => new("The request canceled successfully!", 994);
|
||||||
|
|
||||||
|
public static ServiceError NotFound => new("The specified resource was not found.", 990);
|
||||||
|
|
||||||
|
public static ServiceError ValidationFormat => new("Request object format is not true.", 901);
|
||||||
|
|
||||||
|
public static ServiceError Validation => new("One or more validation errors occurred.", 900);
|
||||||
|
|
||||||
|
public static ServiceError SearchAtLeastOneCharacter =>
|
||||||
|
new("Search parameter must have at least one character!", 898);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default error for when we receive an exception
|
||||||
|
/// </summary>
|
||||||
|
public static ServiceError ServiceProviderNotFound =>
|
||||||
|
new("Service Provider with this name does not exist.", 700);
|
||||||
|
|
||||||
|
public static ServiceError ServiceProvider => new("Service Provider failed to return as expected.", 600);
|
||||||
|
|
||||||
|
public static ServiceError DateTimeFormatError =>
|
||||||
|
new("Date format is not true. Date format must be like yyyy-MM-dd (2019-07-19)", 500);
|
||||||
|
|
||||||
|
#region Override Equals Operator
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use this to compare if two errors are equal
|
||||||
|
/// Ref: https://msdn.microsoft.com/ru-ru/library/ms173147(v=vs.80).aspx
|
||||||
|
/// </summary>
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
// If parameter cannot be cast to ServiceError or is null return false.
|
||||||
|
var error = obj as ServiceError;
|
||||||
|
|
||||||
|
// Return true if the error codes match. False if the object we're comparing to is null
|
||||||
|
// or if it has a different code.
|
||||||
|
return Code == error?.Code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(ServiceError error)
|
||||||
|
{
|
||||||
|
// Return true if the error codes match. False if the object we're comparing to is null
|
||||||
|
// or if it has a different code.
|
||||||
|
return Code == error?.Code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(ServiceError? a, ServiceError? b)
|
||||||
|
{
|
||||||
|
// If both are null, or both are same instance, return true.
|
||||||
|
if (ReferenceEquals(a, b))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If one is null, but not both, return false.
|
||||||
|
if (a is null || b is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if the fields match:
|
||||||
|
return a.Equals(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(ServiceError a, ServiceError b)
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
65
Application/Common/Models/ServiceResult.cs
Normal file
65
Application/Common/Models/ServiceResult.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
namespace Application.Common.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A standard response for service calls.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Return data type</typeparam>
|
||||||
|
public class ServiceResult<T> : ServiceResult
|
||||||
|
{
|
||||||
|
public T? Data { get; set; }
|
||||||
|
|
||||||
|
public ServiceResult(T? data)
|
||||||
|
{
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceResult(T? data, ServiceError error) : base(error)
|
||||||
|
{
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceResult(ServiceError error) : base(error)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ServiceResult
|
||||||
|
{
|
||||||
|
public bool Succeeded => Error == null;
|
||||||
|
|
||||||
|
public ServiceError? Error { get; set; }
|
||||||
|
|
||||||
|
public ServiceResult(ServiceError? error)
|
||||||
|
{
|
||||||
|
error ??= ServiceError.DefaultError;
|
||||||
|
|
||||||
|
Error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceResult() { }
|
||||||
|
|
||||||
|
#region Helper Methods
|
||||||
|
|
||||||
|
public static ServiceResult Failed(ServiceError error)
|
||||||
|
{
|
||||||
|
return new ServiceResult(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServiceResult<T> Failed<T>(ServiceError error)
|
||||||
|
{
|
||||||
|
return new ServiceResult<T>(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServiceResult<T> Failed<T>(T data, ServiceError error)
|
||||||
|
{
|
||||||
|
return new ServiceResult<T>(data, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServiceResult<T> Success<T>(T data)
|
||||||
|
{
|
||||||
|
return new ServiceResult<T>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
@ -1,4 +1,7 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Application.Common.Behaviours;
|
||||||
|
using Application.Game.Card;
|
||||||
|
using Application.Interfaces;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
@ -10,6 +13,8 @@ public static class DependencyInjection
|
|||||||
{
|
{
|
||||||
services.AddMediatR(Assembly.GetExecutingAssembly());
|
services.AddMediatR(Assembly.GetExecutingAssembly());
|
||||||
|
|
||||||
|
services.AddScoped<ICardDependencyAggregate, CardDependencyAggregate>();
|
||||||
|
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>));
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System.Xml.Serialization;
|
using System.ComponentModel;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
namespace Application.Dto;
|
namespace Application.Dto;
|
||||||
|
|
||||||
@ -8,6 +9,7 @@ public class CardDto
|
|||||||
public long CardId { get; set; }
|
public long CardId { get; set; }
|
||||||
|
|
||||||
[XmlElement(ElementName = "player_name")]
|
[XmlElement(ElementName = "player_name")]
|
||||||
|
[DefaultValue("")]
|
||||||
public string PlayerName { get; set; } = string.Empty;
|
public string PlayerName { get; set; } = string.Empty;
|
||||||
|
|
||||||
[XmlElement("score_i1")]
|
[XmlElement("score_i1")]
|
||||||
@ -23,11 +25,14 @@ public class CardDto
|
|||||||
public long Fcol3 { get; set; }
|
public long Fcol3 { get; set; }
|
||||||
|
|
||||||
[XmlElement("achieve_status")]
|
[XmlElement("achieve_status")]
|
||||||
|
[DefaultValue("")]
|
||||||
public string AchieveStatus { get; set; } = string.Empty;
|
public string AchieveStatus { get; set; } = string.Empty;
|
||||||
|
|
||||||
[XmlElement("created")]
|
[XmlElement("created")]
|
||||||
|
[DefaultValue("")]
|
||||||
public string Created { get; set; } = string.Empty;
|
public string Created { get; set; } = string.Empty;
|
||||||
|
|
||||||
[XmlElement("modified")]
|
[XmlElement("modified")]
|
||||||
|
[DefaultValue("")]
|
||||||
public string Modified { get; set; } = string.Empty;
|
public string Modified { get; set; } = string.Empty;
|
||||||
}
|
}
|
15
Application/Game/Card/CardDependencyAggregate.cs
Normal file
15
Application/Game/Card/CardDependencyAggregate.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using Application.Interfaces;
|
||||||
|
|
||||||
|
namespace Application.Game.Card;
|
||||||
|
|
||||||
|
public class CardDependencyAggregate : ICardDependencyAggregate
|
||||||
|
{
|
||||||
|
public CardDependencyAggregate(ICardDbContext cardDbContext, IMusicDbContext musicDbContext)
|
||||||
|
{
|
||||||
|
CardDbContext = cardDbContext;
|
||||||
|
MusicDbContext = musicDbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICardDbContext CardDbContext { get; }
|
||||||
|
public IMusicDbContext MusicDbContext { get; }
|
||||||
|
}
|
36
Application/Game/Card/CardRegisterCommand.cs
Normal file
36
Application/Game/Card/CardRegisterCommand.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using Application.Common;
|
||||||
|
using Application.Common.Exceptions;
|
||||||
|
using Application.Common.Models;
|
||||||
|
using Application.Dto;
|
||||||
|
using Application.Interfaces;
|
||||||
|
using Application.Mappers;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Throw;
|
||||||
|
|
||||||
|
namespace Application.Game.Card;
|
||||||
|
|
||||||
|
public record CardRegisterCommand(long CardId, string Data) : IRequestWrapper<string>;
|
||||||
|
|
||||||
|
public class CardRegisterCommandHandler : CardRequestHandlerBase<CardRegisterCommand, string>
|
||||||
|
{
|
||||||
|
public CardRegisterCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<ServiceResult<string>> Handle(CardRegisterCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var exists = CardDbContext.CardMains.Any(card => card.CardId == request.CardId);
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
return ServiceResult.Failed<string>(ServiceError.CustomMessage($"Card {request.CardId} already exists!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var card = request.Data.DeserializeCardData<CardDto>().CardDtoToCardMain();
|
||||||
|
card.CardId = request.CardId;
|
||||||
|
CardDbContext.CardMains.Add(card);
|
||||||
|
await CardDbContext.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
return new ServiceResult<string>(request.Data);
|
||||||
|
}
|
||||||
|
}
|
23
Application/Game/Card/CardRequest.cs
Normal file
23
Application/Game/Card/CardRequest.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using Domain;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
|
||||||
|
namespace Application.Game.Card;
|
||||||
|
|
||||||
|
public class CardRequest
|
||||||
|
{
|
||||||
|
[ModelBinder(Name = "mac_addr")]
|
||||||
|
public string Mac { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[ModelBinder(Name = "cmd_str")]
|
||||||
|
public int CardCommandType { get; set; }
|
||||||
|
|
||||||
|
[ModelBinder(Name = "type")]
|
||||||
|
public int CardRequestType { get; set; }
|
||||||
|
|
||||||
|
[ModelBinder(Name = "card_no")]
|
||||||
|
public long CardId { get; set; }
|
||||||
|
|
||||||
|
[ModelBinder(Name = "data")]
|
||||||
|
public string Data { get; set; } = string.Empty;
|
||||||
|
}
|
21
Application/Game/Card/CardRequestHandlerBase.cs
Normal file
21
Application/Game/Card/CardRequestHandlerBase.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Application.Common.Models;
|
||||||
|
using Application.Interfaces;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Application.Game.Card;
|
||||||
|
|
||||||
|
public abstract class CardRequestHandlerBase<TIn, TOut>: IRequestHandlerWrapper<TIn, TOut>
|
||||||
|
where TIn : IRequestWrapper<TOut>
|
||||||
|
{
|
||||||
|
public ICardDbContext CardDbContext { get; }
|
||||||
|
public IMusicDbContext MusicDbContext { get; }
|
||||||
|
|
||||||
|
public CardRequestHandlerBase(ICardDependencyAggregate aggregate)
|
||||||
|
{
|
||||||
|
CardDbContext = aggregate.CardDbContext;
|
||||||
|
MusicDbContext = aggregate.MusicDbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Task<ServiceResult<TOut>> Handle(TIn request, CancellationToken cancellationToken);
|
||||||
|
}
|
9
Application/Interfaces/ICardDependencyAggregate.cs
Normal file
9
Application/Interfaces/ICardDependencyAggregate.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Application.Interfaces;
|
||||||
|
|
||||||
|
public interface ICardDependencyAggregate
|
||||||
|
{
|
||||||
|
ICardDbContext CardDbContext { get; }
|
||||||
|
IMusicDbContext MusicDbContext { get; }
|
||||||
|
}
|
14
Application/Interfaces/IRequestWrapper.cs
Normal file
14
Application/Interfaces/IRequestWrapper.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using Application.Common.Models;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace Application.Interfaces;
|
||||||
|
|
||||||
|
public interface IRequestWrapper<T> : IRequest<ServiceResult<T>>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IRequestHandlerWrapper<TIn, TOut> : IRequestHandler<TIn, ServiceResult<TOut>> where TIn : IRequestWrapper<TOut>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -8,4 +8,6 @@ namespace Application.Mappers;
|
|||||||
public static partial class CardMapper
|
public static partial class CardMapper
|
||||||
{
|
{
|
||||||
public static partial CardDto CardMainToCardDto(this CardMain cardMain);
|
public static partial CardDto CardMainToCardDto(this CardMain cardMain);
|
||||||
|
|
||||||
|
public static partial CardMain CardDtoToCardMain(this CardDto cardDto);
|
||||||
}
|
}
|
20
Domain/Config/GameConfig.cs
Normal file
20
Domain/Config/GameConfig.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
namespace Domain.Config;
|
||||||
|
|
||||||
|
public class GameConfig
|
||||||
|
{
|
||||||
|
public const string GAME_SECTION = "Game";
|
||||||
|
|
||||||
|
public int AvatarCount { get; set; }
|
||||||
|
|
||||||
|
public int NavigatorCount { get; set; }
|
||||||
|
|
||||||
|
public int ItemCount { get; set; }
|
||||||
|
|
||||||
|
public int SkinCount { get; set; }
|
||||||
|
|
||||||
|
public int SeCount { get; set; }
|
||||||
|
|
||||||
|
public int TitleCount { get; set; }
|
||||||
|
|
||||||
|
public List<int> UnlockableSongIds { get; set; } = new();
|
||||||
|
}
|
9
Domain/Enums/CardCommandType.cs
Normal file
9
Domain/Enums/CardCommandType.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Domain;
|
||||||
|
|
||||||
|
public enum CardCommandType
|
||||||
|
{
|
||||||
|
CardReadRequest = 256,
|
||||||
|
CardWriteRequest = 768,
|
||||||
|
RegisterRequest = 512,
|
||||||
|
ReissueRequest = 1536
|
||||||
|
}
|
55
Domain/Enums/CardRequestType.cs
Normal file
55
Domain/Enums/CardRequestType.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
namespace Domain;
|
||||||
|
|
||||||
|
public enum CardRequestType
|
||||||
|
{
|
||||||
|
#region Read
|
||||||
|
ReadCard = 259,
|
||||||
|
ReadCardDetail = 260,
|
||||||
|
ReadCardDetails = 261,
|
||||||
|
ReadCardBData = 264,
|
||||||
|
ReadAvatar = 418,
|
||||||
|
ReadItem = 420,
|
||||||
|
ReadSkin = 422,
|
||||||
|
ReadTitle = 424,
|
||||||
|
ReadMusic = 428,
|
||||||
|
ReadEventReward = 441,
|
||||||
|
ReadNavigator = 443,
|
||||||
|
ReadMusicExtra = 465,
|
||||||
|
ReadMusicAou = 467,
|
||||||
|
ReadCoin = 468,
|
||||||
|
ReadUnlockReward = 507,
|
||||||
|
ReadUnlockKeynum = 509,
|
||||||
|
ReadSoundEffect = 8458,
|
||||||
|
ReadGetMessage = 8461,
|
||||||
|
ReadCond = 8465,
|
||||||
|
ReadTotalTrophy = 8468,
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Session
|
||||||
|
GetSession = 401,
|
||||||
|
StartSession = 402,
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region Write
|
||||||
|
WriteCard = 771,
|
||||||
|
WriteCardDetail = 772,
|
||||||
|
WriteCardBData = 776,
|
||||||
|
WriteAvatar = 929,
|
||||||
|
WriteItem = 931,
|
||||||
|
WriteTitle = 935,
|
||||||
|
WriteMusicDetail = 941,
|
||||||
|
WriteNavigator = 954,
|
||||||
|
WriteCoin = 980,
|
||||||
|
WriteSkin = 933,
|
||||||
|
WriteUnlockKeynum = 1020,
|
||||||
|
WriteSoundEffect = 8969,
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region Online Matching
|
||||||
|
StartOnlineMatching = 8705,
|
||||||
|
UpdateOnlineMatching = 8961,
|
||||||
|
UploadOnlineMatchingResult = 8709,
|
||||||
|
#endregion
|
||||||
|
}
|
27
Domain/Enums/CardReturnCode.cs
Normal file
27
Domain/Enums/CardReturnCode.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace Domain;
|
||||||
|
|
||||||
|
public enum CardReturnCode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Normal
|
||||||
|
/// 処理は正常に完了しました in debug string
|
||||||
|
/// </summary>
|
||||||
|
Ok = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// New card
|
||||||
|
/// 未登録のカードです in debug string
|
||||||
|
/// </summary>
|
||||||
|
CardNotRegistered = 23,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Not reissue, to determine whether it is a new card or reissued card
|
||||||
|
/// 再発行予約がありません in debug string
|
||||||
|
/// </summary>
|
||||||
|
NotReissue = 27,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Server side validation error
|
||||||
|
/// </summary>
|
||||||
|
Unknown = 999
|
||||||
|
}
|
@ -10,77 +10,89 @@ namespace Infrastructure.Migrations
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.CreateTable(
|
if (!MigrationHelper.Exists("card_bdata"))
|
||||||
name: "card_bdata",
|
{
|
||||||
columns: table => new
|
migrationBuilder.CreateTable(
|
||||||
{
|
name: "card_bdata",
|
||||||
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
columns: table => new
|
||||||
bdata = table.Column<string>(type: "TEXT", nullable: true),
|
{
|
||||||
bdatasize = table.Column<long>(name: "bdata_size", type: "INTEGER", nullable: false)
|
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||||
},
|
bdata = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
constraints: table =>
|
bdatasize = table.Column<long>(name: "bdata_size", type: "INTEGER", nullable: false)
|
||||||
{
|
},
|
||||||
table.PrimaryKey("PK_card_bdata", x => x.cardid);
|
constraints: table =>
|
||||||
});
|
{
|
||||||
|
table.PrimaryKey("PK_card_bdata", x => x.cardid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
if (!MigrationHelper.Exists("card_detail"))
|
||||||
name: "card_detail",
|
{
|
||||||
columns: table => new
|
migrationBuilder.CreateTable(
|
||||||
{
|
name: "card_detail",
|
||||||
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
columns: table => new
|
||||||
pcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
{
|
||||||
pcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||||
pcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
pcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
scorei1 = table.Column<long>(name: "score_i1", type: "INTEGER", nullable: false),
|
pcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
scoreui1 = table.Column<long>(name: "score_ui1", type: "INTEGER", nullable: false),
|
pcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
scoreui2 = table.Column<long>(name: "score_ui2", type: "INTEGER", nullable: false),
|
scorei1 = table.Column<long>(name: "score_i1", type: "INTEGER", nullable: false),
|
||||||
scoreui3 = table.Column<long>(name: "score_ui3", type: "INTEGER", nullable: false),
|
scoreui1 = table.Column<long>(name: "score_ui1", type: "INTEGER", nullable: false),
|
||||||
scoreui4 = table.Column<long>(name: "score_ui4", type: "INTEGER", nullable: false),
|
scoreui2 = table.Column<long>(name: "score_ui2", type: "INTEGER", nullable: false),
|
||||||
scoreui5 = table.Column<long>(name: "score_ui5", type: "INTEGER", nullable: false),
|
scoreui3 = table.Column<long>(name: "score_ui3", type: "INTEGER", nullable: false),
|
||||||
scoreui6 = table.Column<long>(name: "score_ui6", type: "INTEGER", nullable: false),
|
scoreui4 = table.Column<long>(name: "score_ui4", type: "INTEGER", nullable: false),
|
||||||
scorebi1 = table.Column<long>(name: "score_bi1", type: "INTEGER", nullable: false),
|
scoreui5 = table.Column<long>(name: "score_ui5", type: "INTEGER", nullable: false),
|
||||||
lastplaytenpoid = table.Column<string>(name: "last_play_tenpo_id", type: "TEXT", nullable: true),
|
scoreui6 = table.Column<long>(name: "score_ui6", type: "INTEGER", nullable: false),
|
||||||
fcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
scorebi1 = table.Column<long>(name: "score_bi1", type: "INTEGER", nullable: false),
|
||||||
fcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
lastplaytenpoid = table.Column<string>(name: "last_play_tenpo_id", type: "TEXT", nullable: true),
|
||||||
fcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
fcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
lastplaytime = table.Column<long>(name: "last_play_time", type: "INTEGER", nullable: false)
|
fcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
},
|
fcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
constraints: table =>
|
lastplaytime = table.Column<long>(name: "last_play_time", type: "INTEGER", nullable: false)
|
||||||
{
|
},
|
||||||
table.PrimaryKey("PK_card_detail", x => new { x.cardid, x.pcol1, x.pcol2, x.pcol3 });
|
constraints: table =>
|
||||||
});
|
{
|
||||||
|
table.PrimaryKey("PK_card_detail", x => new { x.cardid, x.pcol1, x.pcol2, x.pcol3 });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
if (!MigrationHelper.Exists("card_main"))
|
||||||
name: "card_main",
|
{
|
||||||
columns: table => new
|
migrationBuilder.CreateTable(
|
||||||
{
|
name: "card_main",
|
||||||
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
columns: table => new
|
||||||
playername = table.Column<string>(name: "player_name", type: "TEXT", nullable: false),
|
{
|
||||||
scorei1 = table.Column<long>(name: "score_i1", type: "INTEGER", nullable: false),
|
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||||
fcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
playername = table.Column<string>(name: "player_name", type: "TEXT", nullable: false),
|
||||||
fcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
scorei1 = table.Column<long>(name: "score_i1", type: "INTEGER", nullable: false),
|
||||||
fcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
fcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
achievestatus = table.Column<string>(name: "achieve_status", type: "TEXT", nullable: false),
|
fcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
created = table.Column<string>(type: "TEXT", nullable: true),
|
fcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
modified = table.Column<string>(type: "TEXT", nullable: true)
|
achievestatus = table.Column<string>(name: "achieve_status", type: "TEXT", nullable: false),
|
||||||
},
|
created = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
constraints: table =>
|
modified = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
{
|
},
|
||||||
table.PrimaryKey("PK_card_main", x => x.cardid);
|
constraints: table =>
|
||||||
});
|
{
|
||||||
|
table.PrimaryKey("PK_card_main", x => x.cardid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
if (!MigrationHelper.Exists("CardPlayCount"))
|
||||||
name: "CardPlayCount",
|
{
|
||||||
columns: table => new
|
migrationBuilder.CreateTable(
|
||||||
{
|
name: "CardPlayCount",
|
||||||
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
columns: table => new
|
||||||
playcount = table.Column<long>(name: "play_count", type: "INTEGER", nullable: false),
|
{
|
||||||
lastplayedtime = table.Column<long>(name: "last_played_time", type: "INTEGER", nullable: false)
|
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||||
},
|
playcount = table.Column<long>(name: "play_count", type: "INTEGER", nullable: false),
|
||||||
constraints: table =>
|
lastplayedtime = table.Column<long>(name: "last_played_time", type: "INTEGER", nullable: false)
|
||||||
{
|
},
|
||||||
table.PrimaryKey("PK_CardPlayCount", x => x.cardid);
|
constraints: table =>
|
||||||
});
|
{
|
||||||
|
table.PrimaryKey("PK_CardPlayCount", x => x.cardid);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -20,14 +20,14 @@ public static class MigrationHelper
|
|||||||
|
|
||||||
return reader.Read()? (long)reader[0] == 1 : false;
|
return reader.Read()? (long)reader[0] == 1 : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetConnectionString()
|
private static string GetConnectionString()
|
||||||
{
|
{
|
||||||
var builder = new ConfigurationBuilder()
|
var builder = new ConfigurationBuilder()
|
||||||
.SetBasePath(PathHelper.ConfigurationPath)
|
.SetBasePath(PathHelper.ConfigurationPath)
|
||||||
.AddJsonFile("database.json", optional: false, reloadOnChange: false);
|
.AddJsonFile("database.json", optional: false, reloadOnChange: false);
|
||||||
|
|
||||||
var cardDbName = builder.Build()["CardDbName"];
|
var cardDbName = builder.Build()["CardDbName"] ?? "card.db3";
|
||||||
var cardDbPath = Path.Combine(PathHelper.DatabasePath, cardDbName);
|
var cardDbPath = Path.Combine(PathHelper.DatabasePath, cardDbName);
|
||||||
return cardDbPath;
|
return cardDbPath;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
{
|
{
|
||||||
"AvatarCount": 356,
|
"Game": {
|
||||||
"NavigatorCount": 118,
|
"AvatarCount": 356,
|
||||||
"ItemCount": 21,
|
"NavigatorCount": 118,
|
||||||
"SkinCount": 21,
|
"ItemCount": 21,
|
||||||
"SeCount": 26,
|
"SkinCount": 21,
|
||||||
"TitleCount": 5530,
|
"SeCount": 26,
|
||||||
"UnlockableSongIds":
|
"TitleCount": 5530,
|
||||||
[
|
"UnlockableSongIds":
|
||||||
11, 13, 149, 273, 291, 320, 321, 371, 378, 384, 464, 471, 474, 475, 492,
|
[
|
||||||
494, 498, 520, 548, 551, 558, 561, 565, 570, 577, 583, 612, 615, 622,
|
11, 13, 149, 273, 291, 320, 321, 371, 378, 384, 464, 471, 474, 475, 492,
|
||||||
632, 659, 666, 668, 670, 672, 676, 680, 682, 685, 686, 697, 700, 701,
|
494, 498, 520, 548, 551, 558, 561, 565, 570, 577, 583, 612, 615, 622,
|
||||||
711, 720, 749, 875, 876, 877
|
632, 659, 666, 668, 670, 672, 676, 680, 682, 685, 686, 697, 700, 701,
|
||||||
]
|
711, 720, 749, 875, 876, 877
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
@ -20,5 +20,20 @@ namespace MainServer.Controllers.API
|
|||||||
{
|
{
|
||||||
return context.MusicUnlocks.First();
|
return context.MusicUnlocks.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public ActionResult<string> TestXmlInputOutput([FromForm(Name = "my_model")]TestModel model,
|
||||||
|
[FromForm(Name = "my_type")]int type)
|
||||||
|
{
|
||||||
|
return Ok($"{model.Name}\n{model.Age}\n{type}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TestModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public int Age { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
134
MainServer/Controllers/Game/CardController.cs
Normal file
134
MainServer/Controllers/Game/CardController.cs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
using System.Net;
|
||||||
|
using Application.Common.Models;
|
||||||
|
using Application.Game.Card;
|
||||||
|
using Domain;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Throw;
|
||||||
|
|
||||||
|
namespace MainServer.Controllers.Game;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("service/card")]
|
||||||
|
public class CardController : BaseController<CardController>
|
||||||
|
{
|
||||||
|
[HttpPost("cardn.cgi")]
|
||||||
|
public async Task<ActionResult<string>> CardService([FromForm]CardRequest request)
|
||||||
|
{
|
||||||
|
var cardRequestType = (CardRequestType)request.CardRequestType;
|
||||||
|
var cardCommandType = (CardCommandType)request.CardCommandType;
|
||||||
|
|
||||||
|
cardCommandType.Throw().IfOutOfRange();
|
||||||
|
if (cardCommandType is CardCommandType.CardReadRequest or CardCommandType.CardWriteRequest)
|
||||||
|
{
|
||||||
|
cardRequestType.Throw().IfOutOfRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Data = WebUtility.UrlDecode(request.Data);
|
||||||
|
var result = ServiceResult.Failed<string>(ServiceError.DefaultError);
|
||||||
|
switch (cardCommandType)
|
||||||
|
{
|
||||||
|
case CardCommandType.CardReadRequest:
|
||||||
|
{
|
||||||
|
switch (cardRequestType)
|
||||||
|
{
|
||||||
|
case CardRequestType.ReadCard:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadCardDetail:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadCardDetails:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadCardBData:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadAvatar:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadItem:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadSkin:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadTitle:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadMusic:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadEventReward:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadNavigator:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadMusicExtra:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadMusicAou:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadCoin:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadUnlockReward:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadUnlockKeynum:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadSoundEffect:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadGetMessage:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadCond:
|
||||||
|
break;
|
||||||
|
case CardRequestType.ReadTotalTrophy:
|
||||||
|
break;
|
||||||
|
case CardRequestType.GetSession:
|
||||||
|
break;
|
||||||
|
case CardRequestType.StartSession:
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteCard:
|
||||||
|
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteCardDetail:
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteCardBData:
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteAvatar:
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteItem:
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteTitle:
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteMusicDetail:
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteNavigator:
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteCoin:
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteSkin:
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteUnlockKeynum:
|
||||||
|
break;
|
||||||
|
case CardRequestType.WriteSoundEffect:
|
||||||
|
break;
|
||||||
|
case CardRequestType.StartOnlineMatching:
|
||||||
|
break;
|
||||||
|
case CardRequestType.UpdateOnlineMatching:
|
||||||
|
break;
|
||||||
|
case CardRequestType.UploadOnlineMatchingResult:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(message: "Should not happen", paramName:null);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CardCommandType.CardWriteRequest:
|
||||||
|
break;
|
||||||
|
case CardCommandType.RegisterRequest:
|
||||||
|
result = await Mediator.Send(new CardRegisterCommand(request.CardId, request.Data));
|
||||||
|
break;
|
||||||
|
case CardCommandType.ReissueRequest:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(message: "Should not happen", paramName:null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
return Ok(result.Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorMessage = $"{(int)CardReturnCode.Unknown}\n" +
|
||||||
|
$"{result.Error!.Message}";
|
||||||
|
return Ok(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
81
MainServer/Filters/ApiExceptionFilterAttributes.cs
Normal file
81
MainServer/Filters/ApiExceptionFilterAttributes.cs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Application.Common.Models;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
||||||
|
namespace MainServer.Filters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exception filters
|
||||||
|
/// </summary>
|
||||||
|
public class ApiExceptionFilterService : ExceptionFilterAttribute
|
||||||
|
{
|
||||||
|
private readonly IDictionary<Type, Action<ExceptionContext>> exceptionHandlers;
|
||||||
|
|
||||||
|
private readonly ILogger<ApiExceptionFilterService> logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
public ApiExceptionFilterService(ILogger<ApiExceptionFilterService> logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
// Register known exception types and handlers.
|
||||||
|
exceptionHandlers = new Dictionary<Type, Action<ExceptionContext>>
|
||||||
|
{
|
||||||
|
{ typeof(ArgumentOutOfRangeException), HandleArgumentOutOfRangeException }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// On exception event
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public override void OnException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
HandleException(context);
|
||||||
|
|
||||||
|
base.OnException(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
var type = context.Exception.GetType();
|
||||||
|
if (exceptionHandlers.ContainsKey(type))
|
||||||
|
{
|
||||||
|
exceptionHandlers[type].Invoke(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleUnknownException(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleUnknownException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
var details = ServiceResult.Failed(ServiceError.DefaultError);
|
||||||
|
|
||||||
|
context.Result = new ObjectResult(details)
|
||||||
|
{
|
||||||
|
StatusCode = StatusCodes.Status500InternalServerError
|
||||||
|
};
|
||||||
|
|
||||||
|
context.ExceptionHandled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleArgumentOutOfRangeException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
logger.LogError(context.Exception, "");
|
||||||
|
var exception = context.Exception as ArgumentOutOfRangeException;
|
||||||
|
Debug.Assert(exception != null, nameof(exception) + " != null");
|
||||||
|
|
||||||
|
var variable = exception.ParamName ?? "Unknown";
|
||||||
|
var details = ServiceResult.Failed(ServiceError.CustomMessage($"Argument {variable} out of bounds!"));
|
||||||
|
|
||||||
|
context.Result = new ObjectResult(details)
|
||||||
|
{
|
||||||
|
StatusCode = StatusCodes.Status400BadRequest
|
||||||
|
};
|
||||||
|
|
||||||
|
context.ExceptionHandled = true;
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Controllers\API" />
|
<Folder Include="Controllers\API" />
|
||||||
|
<Folder Include="Logs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -5,6 +5,7 @@ using Domain.Config;
|
|||||||
using Infrastructure;
|
using Infrastructure;
|
||||||
using Infrastructure.Common;
|
using Infrastructure.Common;
|
||||||
using Infrastructure.Persistence;
|
using Infrastructure.Persistence;
|
||||||
|
using MainServer.Filters;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Extensions.Logging;
|
using Serilog.Extensions.Logging;
|
||||||
@ -38,6 +39,8 @@ try
|
|||||||
builder.Configuration.GetSection(EventConfig.EVENT_SECTION));
|
builder.Configuration.GetSection(EventConfig.EVENT_SECTION));
|
||||||
builder.Services.Configure<RelayConfig>(
|
builder.Services.Configure<RelayConfig>(
|
||||||
builder.Configuration.GetSection(RelayConfig.RELAY_SECTION));
|
builder.Configuration.GetSection(RelayConfig.RELAY_SECTION));
|
||||||
|
builder.Services.Configure<GameConfig>(
|
||||||
|
builder.Configuration.GetSection(GameConfig.GAME_SECTION));
|
||||||
|
|
||||||
var serverIp = builder.Configuration["ServerIp"] ?? "127.0.0.1";
|
var serverIp = builder.Configuration["ServerIp"] ?? "127.0.0.1";
|
||||||
var certificateManager = new CertificateService(serverIp, new SerilogLoggerFactory(Log.Logger).CreateLogger(""));
|
var certificateManager = new CertificateService(serverIp, new SerilogLoggerFactory(Log.Logger).CreateLogger(""));
|
||||||
@ -51,7 +54,9 @@ try
|
|||||||
configuration.WriteTo.Console().ReadFrom.Configuration(context.Configuration);
|
configuration.WriteTo.Console().ReadFrom.Configuration(context.Configuration);
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddControllers().AddXmlSerializerFormatters();
|
builder.Services.AddControllers(options =>
|
||||||
|
options.Filters.Add<ApiExceptionFilterService>());
|
||||||
|
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen();
|
||||||
@ -80,21 +85,23 @@ try
|
|||||||
var eventService = app.Services.GetService<IEventManagerService>();
|
var eventService = app.Services.GetService<IEventManagerService>();
|
||||||
eventService.ThrowIfNull();
|
eventService.ThrowIfNull();
|
||||||
eventService.InitializeEvents();
|
eventService.InitializeEvents();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// app.UseExceptionHandler();
|
||||||
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Fatal(ex, "Unhandled exception");
|
Log.Fatal(ex, "Unhandled exception in startup");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user