Hello i have the next design problem:
Before accessing to my Controller i have a filter to check the authentication and the authorization so in order to do so i have to know the user. Until there everything is perfect, but the problem starts when i want to know the user who is log so i can do more things. Any ideas?
[AdministratorAuth("DogController")]
public class DogController : ControllerBase
{
[HttpGet]
public IAction GetDogsOfUser()
{
return Ok(dogLogic.GetDogsOfUser());
}
}
public class LoginAuth : Attribute, IActionFilter
{
public static Guid Token { get; private set; }
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
string headerToken = context.HttpContext.Request.Headers["Authorization"];
if (headerToken is null)
{
context.Result = new ContentResult()
{
Content = "Token is required",
};
} else
{
try
{
Guid token = Guid.Parse(headerToken);
VerifyToken(token, context);
Token = token;
} catch (FormatException)
{
context.Result = new ContentResult()
{
Content = "Invalid Token format",
};
}
}
}
private void VerifyToken(Guid token, ActionExecutingContext context)
{
using (var sessions = GetSessionLogic(context))
{
if (!sessions.IsValidToken(token))
{
context.Result = new ContentResult()
{
Content = "Invalid Token",
};
}
}
}
private ISessionLogic GetSessionLogic(ActionExecutingContext context)
{
var typeOfSessionsLogic = typeof(ISessionLogic);
return context.HttpContext.RequestServices.GetService(typeOfSessionsLogic) as ISessionLogic;
}
}
public class AdministratorAuth : LoginAuth
{
private readonly string permission;
public AdministratorAuth(string permission)
{
this.permission = permission;
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
string headerToken = context.HttpContext.Request.Headers["Authorization"];
Guid token = Guid.Parse(headerToken);
using (var sessions = GetSessionLogic(context))
{
if (!sessions.HasLevel(token, permission))
{
context.Result = new ContentResult()
{
Content = "The user hasn't the permission to access " + permission,
};
}
}
}
private ISessionLogic GetSessionLogic(ActionExecutingContext context)
{
var typeOfSessionsLogic = typeof(ISessionLogic);
return context.HttpContext.RequestServices.GetService(typeOfSessionsLogic) as ISessionLogic;
}
}
So let's imagine that i have this, if i want to know the dogs of the user who is log, how can i do?
You can simply use Nlog or log4net function,
or
create a model which contains
Logged = DateTime.Now,
LoginHost = Request.Host.ToString(),
LoginIP = Request.HttpContext.Connection.LocalIpAddress.ToString(),
SessionId = Request.HttpContext.Session.Id
Related
I am trying to capture the exception when the route is not found and wrap the exception with the generic response model.
I tried to implement, as given in the answer to the question, but this solution also doesn't seem to work in my use case.
Because the status code 404 is also added to the response when the resource is not found, like when Id is not found.
app.UseStatusCodePages(new StatusCodePagesOptions()
{
HandleAsync = (ctx) =>
{
if (ctx.HttpContext.Response.StatusCode == 404)
{
throw new RouteNotFoundException("Route not found");
}
return Task.FromResult(0);
}
})
RouteNotFoundException
public class RouteNotFoundException : Exception
{
public RouteNotFoundException()
: base()
{
}
public RouteNotFoundException(string message)
: base(message)
{
}
}
ApiExceptionFilterAttribute
public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly IDictionary<Type, Action<ExceptionContext>> _exceptionHandlers;
public ApiExceptionFilterAttribute()
{
// Register known exception types and handlers.
_exceptionHandlers = new Dictionary<Type, Action<ExceptionContext>>
{
{ typeof(RouteNotFoundException), HandleNotFoundException }
};
}
public override void OnException(ExceptionContext context)
{
HandleException(context);
base.OnException(context);
}
private void HandleException(ExceptionContext context)
{
Type type = context.Exception.GetType();
if (_exceptionHandlers.ContainsKey(type))
{
_exceptionHandlers[type].Invoke(context);
return;
}
HandleUnknownException(context);
}
private void HandleNotFoundException(ExceptionContext context)
{
var exception = context.Exception as RouteNotFoundException;
var details = new ProblemDetails()
{
Type = "https://tools.ietf.org/html/rfc7231#section-6.5.4",
Title = "The specified resource was not found.",
Detail = exception.Message
};
context.Result = new NotFoundObjectResult(details);
context.ExceptionHandled = true;
}
private void HandleUnknownException(ExceptionContext context)
{
var details = new ProblemDetails
{
Status = StatusCodes.Status500InternalServerError,
Title = "An error occurred while processing your request.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.1"
};
context.Result = new ObjectResult(details)
{
StatusCode = StatusCodes.Status500InternalServerError
};
context.ExceptionHandled = true;
}
}
ResponseWrapperMiddleware
public class ResponseWrapperMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ResponseWrapperMiddleware> _logger;
public ResponseWrapperMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
_logger = loggerFactory?.CreateLogger<ResponseWrapperMiddleware>() ?? throw new ArgumentNullException(nameof(loggerFactory));
}
public async Task Invoke(HttpContext httpContext)
{
try
{
var currentBody = httpContext.Response.Body;
using (var memoryStream = new MemoryStream())
{
//set the current response to the memorystream.
httpContext.Response.Body = memoryStream;
await _next(httpContext);
//reset the body
httpContext.Response.Body = currentBody;
memoryStream.Seek(0, SeekOrigin.Begin);
var readToEnd = new StreamReader(memoryStream).ReadToEnd();
var objResult = JsonConvert.DeserializeObject(readToEnd);
var result = CommonApiResponse.Create((HttpStatusCode)httpContext.Response.StatusCode, objResult, null);
await httpContext.Response.WriteAsync(JsonConvert.SerializeObject(result));
}
}
catch (Exception ex)
{
if (httpContext.Response.HasStarted)
{
_logger.LogWarning("The response has already started, the http status code middleware will not be executed.");
throw;
}
return;
}
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class ResponseWrapperMiddlewareExtensions
{
public static IApplicationBuilder UseResponseWrapperMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ResponseWrapperMiddleware>();
}
}
Generic Response Model
public class CommonApiResponse
{
public static CommonApiResponse Create(HttpStatusCode statusCode, object result = null, string errorMessage = null)
{
return new CommonApiResponse(statusCode, result, errorMessage);
}
public string Version => "1.2.3";
public int StatusCode { get; set; }
public string RequestId { get; }
public string ErrorMessage { get; set; }
public object Result { get; set; }
protected CommonApiResponse(HttpStatusCode statusCode, object result = null, string errorMessage = null)
{
RequestId = Guid.NewGuid().ToString();
StatusCode = (int)statusCode;
Result = result;
ErrorMessage = errorMessage;
}
}
How to handle the error if the route is not found and capture the error in the generic model? What is the workaround for this case?
Welcome everyone
First:
I used this sample: "https://github.com/stevenchang0529/XamarinGoogleDriveRest"
It is a project to explain how to use Google Drive API with XamrinForms with creating a text file, saving it and reading its data, by the way, thanks to Steven Chang.
It works fine, but it asks to log in to the account every time I want to upload the file to Google Drive, how can I make it save the login data after entering it for the first time.
Second:
How can I make the user choose his account from the popup "like the one in the picture below" instead of using the Web Authenticator Native Browser:
the ViewModel class that used in the project:
public class MainViewModel
{
private string scope = "https://www.googleapis.com/auth/drive.file";
private string clientId = "clientId here";
private string redirectUrl = "url:/oauth2redirect";
public ICommand OnGoogleDrive { get; set; }
public MainViewModel()
{
var auth = new OAuth2Authenticator(
this.clientId,
string.Empty,
scope,
new Uri("https://accounts.google.com/o/oauth2/v2/auth"),
new Uri(redirectUrl),
new Uri("https://www.googleapis.com/oauth2/v4/token"),
isUsingNativeUI: true);
AuthenticatorHelper.OAuth2Authenticator = auth;
auth.Completed +=async (sender, e) =>
{
if (e.IsAuthenticated)
{
var initializer = new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new Google.Apis.Auth.OAuth2.ClientSecrets()
{
ClientId = clientId
}
};
initializer.Scopes = new[] { scope };
initializer.DataStore = new FileDataStore("Google.Apis.Auth");
var flow = new GoogleAuthorizationCodeFlow(initializer);
var user = "DriveTest";
var token = new TokenResponse()
{
AccessToken=e.Account.Properties["access_token"],
ExpiresInSeconds=Convert.ToInt64( e.Account.Properties["expires_in"]),
RefreshToken=e.Account.Properties["refresh_token"],
Scope=e.Account.Properties["scope"],
TokenType=e.Account.Properties["token_type"]
};
UserCredential userCredential = new UserCredential(flow, user, token);
var driveService = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = userCredential,
ApplicationName = "Quickstart",
});
//test google drive
DriveServiceHelper helper = new DriveServiceHelper(driveService);
var id = await helper.CreateFile();
await helper.SaveFile(id, "test", "test save content");
var content = await helper.ReadFile(id);
}
};
auth.Error += (sender, e) =>
{
};
this.OnGoogleDrive = new Command(() =>
{
var presenter = new OAuthLoginPresenter();
presenter.Login(auth);
});
}
}
public static class AuthenticatorHelper
{
public static OAuth2Authenticator OAuth2Authenticator { get; set; }
}
I am sorry if there are spelling mistakes, because I'm not good at English.
Even with Xamarin Forms, It won't be 100% like your image on iOS device. I think this is a way to make it similar both iOS and Android platform.
Here is some sample code for that.
Shared
mainpage.xaml
This button is only visible when the user is not logged in.
...
<Button Text="Google Login"
Command="{Binding GoogleLoginCommand}"
IsVisible="{Binding IsLogedIn, Converter={StaticResource InvertBooleanConverter}}"/>
...
InvertBooleanConverter.cs
This converter returns false when IsLogedIn is True and true when IsLogedIn is false.
public class InvertBooleanConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(bool)value;
}
}
Model
GoogleUser.cs
public class GoogleUser
{
public string Name { get; set; }
public string Email { get; set; }
public Uri Picture { get; set; }
}
ViewModel
...
public class MainPageViewModel {
private readonly IGoogleManager _googleManager;
public DelegateCommand GoogleLoginCommand { get; set; }
private bool _isLogedIn;
public bool IsLogedIn
{
get { return _isLogedIn; }
set { _isLogedIn = value; }
}
private GoogleUser _googleUser;
public GoogleUser GoogleUser
{
get { return _googleUser; }
set { _googleUser = value; }
}
...
public MainPageViewModel(IGoogleManager googleManager){ //Without IoC
_googleManager = googleManager;
IsLogedIn = false;
GoogleLoginCommand = new DelegateCommand(GoogleLogin);
}
private void GoogleLogin()
{
_googleManager.Login(OnLoginComplete);
}
private void OnLoginComplete(GoogleUser googleUser, string message)
{
if (googleUser != null){
GoogleUser = googleUser;
IsLogedIn = true;
//Your code after login
} else {
//Error
}
}
...
Interface for each device
public interface IGoogleManager
{
void Login(Action<GoogleUser, string> OnLoginComplete);
void Logout();
}
iOS
YOUR_PROJECT_NAME.ios
Download the GoogleService-info.plist file and add it your Xamarin.iOS folder.
Open the GoogleService-info.plist and copy the REVERSED_CLIENT_ID value
Open the info.plist file, go to the Advanced tab, create a new URL with a editor role and paste it in the URL Scheme field.
AppDelegate.cs
add this code to the AppDelegate class
...
var googleServiceDictionary = NSDictionary.FromFile("GoogleService-Info.plist");
SignIn.SharedInstance.ClientID = googleServiceDictionary["CLIENT_ID"].ToString();
...
GoogleManager.cs
public class GoogleManager : NSObject, IGoogleManager, ISignInDelegate, ISignInUIDelegate
{
private Action<GoogleNativeLogin.Models.GoogleUser, string> _onLoginComplete;
private UIViewController _viewController { get; set; }
public GoogleManager()
{
SignIn.SharedInstance.UIDelegate = this;
SignIn.SharedInstance.Delegate = this;
}
public void Login(Action<GoogleNativeLogin.Models.GoogleUser, string> OnLoginComplete)
{
_onLoginComplete = OnLoginComplete;
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
while (vc.PresentedViewController != null)
{
vc = vc.PresentedViewController;
}
_viewController = vc;
SignIn.SharedInstance.SignInUser();
}
public void Logout()
{
SignIn.SharedInstance.SignOutUser();
}
public void DidSignIn(SignIn signIn, Google.SignIn.GoogleUser user, NSError error)
{
if (user != null && error == null)
_onLoginComplete?.Invoke(new GoogleNativeLogin.Models.GoogleUser()
{
Name = user.Profile.Name,
Email = user.Profile.Email,
Picture = user.Profile.HasImage ? new Uri(user.Profile.GetImageUrl(500).ToString()) : new Uri(string.Empty)
}, string.Empty);
else
_onLoginComplete?.Invoke(null, error.LocalizedDescription);
}
[Export("signIn:didDisconnectWithUser:withError:")]
public void DidDisconnect(SignIn signIn, GoogleUser user, NSError error)
{
// When the user disconnects from app here
}
[Export("signInWillDispatch:error:")]
public void WillDispatch(SignIn signIn, NSError error)
{
//myActivityIndicator.StopAnimating();
}
[Export("signIn:presentViewController:")]
public void PresentViewController(SignIn signIn, UIViewController viewController)
{
_viewController?.PresentViewController(viewController, true, null);
}
[Export("signIn:dismissViewController:")]
public void DismissViewController(SignIn signIn, UIViewController viewController)
{
_viewController?.DismissViewController(true, null);
}
}
Android
MainActivity.cs
add this code to the MainActivity
...
protected override void OnActivityResult(int requestCode, Result resultCode, Android.Content.Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 1)
{
GoogleSignInResult result = Auth.GoogleSignInApi.GetSignInResultFromIntent(data);
GoogleManager.Instance.OnAuthCompleted(result);
}
}
...
GoogleManager
public class GoogleManager : Java.Lang.Object, IGoogleManager, GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener
{
public Action<GoogleUser, string> _onLoginComplete;
public static GoogleApiClient _googleApiClient { get; set; }
public static GoogleManager Instance { get; private set; }
public GoogleManager()
{
Instance = this;
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DefaultSignIn)
.RequestEmail()
.Build();
_googleApiClient = new
GoogleApiClient.Builder(((MainActivity)Forms.Context).ApplicationContext)
.AddConnectionCallbacks(this)
.AddOnConnectionFailedListener(this)
.AddApi(Auth.GOOGLE_SIGN_IN_API, gso)
.AddScope(new Scope(Scopes.Profile))
.Build();
}
public void Login(Action<GoogleUser, string> onLoginComplete)
{
_onLoginComplete = onLoginComplete;
Intent signInIntent = Auth.GoogleSignInApi.GetSignInIntent(_googleApiClient);
((MainActivity)Forms.Context).StartActivityForResult(signInIntent, 1);
_googleApiClient.Connect();
}
public void Logout()
{
_googleApiClient.Disconnect();
}
public void OnAuthCompleted(GoogleSignInResult result)
{
if (result.IsSuccess)
{
GoogleSignInAccount accountt = result.SignInAccount;
_onLoginComplete?.Invoke(new GoogleUser(){
Name = accountt.DisplayName,
Email = accountt.Email,
Picture = new Uri((accountt.PhotoUrl != null ? $ {accountt.PhotoUrl}" : $"//YOUR_PLACE_HOLDER_IMAGE.jpg//"))}, string.Empty);
} else {
_onLoginComplete?.Invoke(null, "An error occured!");
}
}
public void OnConnected(Bundle connectionHint)
{
}
public void OnConnectionSuspended(int cause)
{
_onLoginComplete?.Invoke(null, "Canceled!");
}
public void OnConnectionFailed(ConnectionResult result)
{
_onLoginComplete?.Invoke(null, result.ErrorMessage);
}
}
I'm getting error Unable to cast object of type 'Enterprise.SynCore.Tag to type 'Enterprise.SynCore.DbRow' and I'm unsure of what the issue would be.
Am I missing something in the service or controller somewhere?
DBRow.Slv.cs
public override bool Equals(object x)
{
return Tag == ((DbRow)x).Tag;
}
ActionService.cs
namespace SynApi.Services
{
public interface IActionService
{
DbrAction[] GetActions(string userId, string sessionId);
}
public class ActionService : IActionService
{
private DataContext _context;
public ActionService(DataContext context)
{
_context = context;
}
DbrAction[] IActionService.GetActions(string userId, string sessionId)
{
UserStateHelper ush = new UserStateHelper();
UserState us = ush.CreateUserState(sessionId);
XioTable xt = new XioTable(us, T.User);
DbrUser user = (DbrUser)xt.LoadSingleRow(us.UserTag, C.User_DefaultActionTag);
DbrAction[] actions = { };
List<DbrAction> dbrActions = new List<DbrAction>();
xt = new XioTable(us, T.Action);
xt.SelectCols(C.Action_Description, C.Action_ActionName, C.Action_FolderTag, C.Action_ActionType, C.Action_PrimaryTable);
xt.LoadData();
foreach (DbrAction action in xt.GetAllRows(C.Action_FolderTag))
{
if (!DbrAction.IsTableDeprecated(action.PrimaryTable))
{
if (action.ActionType == ActionType.View || action.ActionType == ActionType.Dashboard)
dbrActions.Add(action);
}
}
us.Completed(LogEntryType.GetStartupProfileData, null);
return dbrActions.ToArray();
}
}
}
ActionsController.cs
public class ActionsController : ControllerBase
{
private IActionService _actionService;
public ActionsController(IActionService actionService)
{
_actionService = actionService;
}
//GET: api/Actions
[HttpGet]
public IActionResult GetActions(string userId, string sessionId)
{
DbrAction[] dbrActions = _actionService.GetActions(userId, sessionId);
return Ok(dbrActions);
}
I have this action filter that I want to call from a function. However, within this filter I am referring to another class called ValidateRoleClient. How can I do a service injection? Or call this class in my function properly?
public class RolesFilterAttribute : ActionFilterAttribute
{
public ValidateRoleClient validateRoleClient;
public string Role { get; set; }
public RolesFilterAttribute(ValidateRoleClient validateRoleClient)
{
this.validateRoleClient = validateRoleClient;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Cookies["Token"] != null || context.HttpContext.Request.Cookies["RefreshToken"] != null)
{
TokenViewModel tvm = new TokenViewModel
{
Token = context.HttpContext.Request.Cookies["Token"],
RefreshToken = context.HttpContext.Request.Cookies["RefreshToken"]
};
ValidateRoleViewModel vrvm = new ValidateRoleViewModel
{
Role = Role,
Token = tvm
};
validateRoleClient.ValidateRole(vrvm);
}
}
}
This is how I refer to the filter in my function
[RolesFilter]
public IActionResult About()
{
return View();
}
The current error I get from this is
Severity Code Description Project File Line Suppression
State Error CS7036 There is no argument given that corresponds to
the required formal parameter 'validateRoleClient' of
'RolesFilterAttribute.RolesFilterAttribute(ValidateRoleClient)'
You could wrap your attribute inside a FilterFactory like so:
public class RolesFilterAttribute : Attribute, IFilterFactory
{
public string Role { get; set; }
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
return new RolesFilterAttributeImplementation(
serviceProvider.GetRequiredService<ValidateRoleClient>(),
Role
);
}
private class RolesFilterAttributeImplementation : ActionFilterAttribute
{
private readonly ValidateRoleClient validateRoleClient;
private readonly string role;
public RolesFilterAttributeImplementation(ValidateRoleClient validateRoleClient, string role)
{
this.validateRoleClient = validateRoleClient;
this.role = role;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Cookies["Token"] != null || context.HttpContext.Request.Cookies["RefreshToken"] != null)
{
TokenViewModel tvm = new TokenViewModel
{
Token = context.HttpContext.Request.Cookies["Token"],
RefreshToken = context.HttpContext.Request.Cookies["RefreshToken"]
};
ValidateRoleViewModel vrvm = new ValidateRoleViewModel
{
Role = role,
Token = tvm
};
validateRoleClient.ValidateRole(vrvm);
}
}
}
public bool IsReusable => false;
}
Of course the ValidateRoleClient service must first be configured to be injected.
You can then use the RolesFilterAttribute attribute as you normally would.
And you can read more about IFilterFactory here: https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.1#ifilterfactory
Below is my implementation. I have written a CustomExceptionFilterAttribute which inherits from ExceptionFilterAttribute. Each error is placed inside a if and else to generate proper result. What I would like to do is create a callback function so that I can remove if and else block and error can be handled in more generic way.
public class HostedServicesController : BaseController
{
public IActioResult Index()
{
throw new NotFoundInDatabaseException("Error in Index Controller");
}
}
public class NotFoundInDatabaseException : Exception
{
public NotFoundInDatabaseException(string objectName, object objectId) :
base(message: $"No {objectName} with id '{objectId}' was found")
{
}
}
public class CustomExceptionFilterAttribute :ExceptionFilterAttribute
{
private SystemManager SysMgr { get; }
public CustomExceptionFilterAttribute(SystemManager systemManager)
{
SysMgr = systemManager;
}
public override void OnException(ExceptionContext context)
{
var le = SysMgr.Logger.NewEntry();
try
{
le.Message = context.Exception.Message;
le.AddException(context.Exception);
var exception = context.Exception;
if (exception is NotFoundInDatabaseException)
{
le.Type = LogType.ClientFaultMinor;
context.Result = new NotFoundObjectResult(new Error(ExceptionCode.ResourceNotFound, exception.Message));
}
else if (exception is ConfigurationException)
{
le.Type = LogType.ErrorMinor;
context.Result = new BadRequestObjectResult(new Error(ExceptionCode.NotAuthorised, exception.Message));
}
else
{
le.Type = LogType.ErrorSevere;
context.Result = new InternalServerErrorObjectResult(new Error(ExceptionCode.Unknown, exception.Message));
}
le.AddProperty("context.Result", context.Result);
//base.OnException(context);
}
finally
{
Task.Run(() => SysMgr.Logger.LogAsync(le)).Wait();
}
}
}
You could use a Dictionary with a custom type for this. Think of something like this:
public class ErrorHandlerData
{
public LogType LogType { get; set; }
public string ExceptionCode { get; set; } // not sure if string
}
public class CustomExceptionFilterAttribute :ExceptionFilterAttribute
{
private static Dictionary<Type, ErrorHandlerData> MyDictionary = new Dictionary<Type, ErrorHandlerData>();
static CustomExceptionFilterAttribute()
{
MyDictionary.Add(typeof(NotFoundInDatabaseException), new ErrorHandlerData
{
LogType = LogType.ClientFaultMinor,
ExceptionCode = ExceptionCode.ResourceNotFound
};
//general catch-all
MyDictionary.Add(typeof(Exception), new ErrorHandlerData
{
LogType = LogType.ErrorSevere,
ExceptionCode = ExceptionCode.Unknown
};
}
So you can then use it like this:
public override void OnException(ExceptionContext context)
{
var le = SysMgr.Logger.NewEntry();
try
{
le.Message = context.Exception.Message;
le.AddException(context.Exception);
var exception = context.Exception;
var exeptionType = exception.GetType();
var errorHandlerData = MyDictionary.ContainsKey(exceptionType) ?
MyDictionary[exceptionType] : MyDictionary[typeof(Exception)];
le.Type = errorHandlerData.LogType;
context.Result = new NotFoundObjectResult(new Error(errorHandlerData.ExceptionCode, exception.Message));
le.AddProperty("context.Result", context.Result);
}
finally
{
Task.Run(() => SysMgr.Logger.LogAsync(le)).Wait();
}
}