Exemple #1
0
        /// <summary>
        /// Collects the tracking information.
        /// </summary>
        public void CollectTrackingInfo(long bodyLength)
        {
            if (Settings != null && Settings.ApiTracking != null && Settings.TrackingEvent && RuntimeContext != null && !RuntimeContext.OmitApiEvent)
            {
                ApiEvent = new ApiEventLog
                {
                    RawUrl     = RawUrl,
                    EntryStamp = EntryStamp,
                    TraceId    = TraceId,
                    // If request came from ApiTransport or other proxy ways, ORIGINAL stands for the IP ADDRESS from original requester.
                    IpAddress          = TryGetRequestHeader(Settings?.OriginalIpAddressHeaderKey.SafeToString(HttpConstants.HttpHeader.ORIGINAL)).SafeToString(ClientIpAddress),
                    CultureCode        = UserLanguages.SafeFirstOrDefault(),
                    ContentLength      = bodyLength,
                    OperatorCredential = ContextHelper.CurrentCredential as BaseCredential,
                    ServiceIdentifier  = RuntimeContext.ApiInstance?.GetType()?.Name,
                    ServerIdentifier   = EnvironmentCore.MachineName
                };

                var clientIdentifierHeaderKey = Settings.ClientIdentifierHeaderKey;
                if (!string.IsNullOrWhiteSpace(clientIdentifierHeaderKey))
                {
                    ApiEvent.ClientIdentifier = TryGetRequestHeader(clientIdentifierHeaderKey);
                }
            }

            if (!string.IsNullOrWhiteSpace(TraceId))
            {
                ApiTraceContext.Initialize(TraceId, TraceSequence, EntryStamp);
            }
        }
Exemple #2
0
        /// <summary>
        /// Invokes as j token.
        /// </summary>
        /// <param name="realm">The realm.</param>
        /// <param name="version">The version.</param>
        /// <param name="httpMethod">The HTTP method.</param>
        /// <param name="resourceName">Name of the resource.</param>
        /// <param name="resourceAction">The resource action.</param>
        /// <param name="key">The key.</param>
        /// <param name="queryString">The query string.</param>
        /// <param name="bodyJson">The body json.</param>
        /// <param name="timeout">The timeout.</param>
        /// <param name="methodNameForTrace">The method name for trace.</param>
        /// <returns>
        /// JToken.
        /// </returns>
        protected JToken InvokeAsJToken(string realm, string version, string httpMethod, string resourceName, string resourceAction, string key = null, Dictionary <string, string> queryString = null, string bodyJson = null, int?timeout = null, [CallerMemberName] string methodNameForTrace = null)
        {
            BaseException exception = null;

            try
            {
                ApiTraceContext.Enter("RestApiClient", methodNameForTrace);

                var httpRequestRaw = CreateHttpRequestRaw(realm, version, httpMethod, resourceName, resourceAction, key, queryString);
                if (httpMethod.IsInString(StringComparison.OrdinalIgnoreCase, HttpConstants.HttpMethod.Post, HttpConstants.HttpMethod.Put))
                {
                    httpRequestRaw.Body = Encoding.UTF8.GetBytes(bodyJson.SafeToString());
                }

                ApiTraceContext.WriteHttpRequestRaw(httpRequestRaw);
                var httpRequest = httpRequestRaw.ToHttpWebRequest();
                if (timeout.HasValue)
                {
                    httpRequest.Timeout = timeout.Value;
                }

                var response = httpRequest.ReadResponseAsObject <JToken>();

                if (response.HttpStatusCode == HttpStatusCode.NoContent)
                {
                    return(null);
                }

                ApiTraceContext.WriteHttpResponseRaw(response);
                return(response.Body);
            }
            catch (HttpOperationException httpEx)
            {
                ApiTraceContext.WriteHttpResponseRaw(new HttpActionResult <string> {
                    Body = httpEx.ExceptionReference.ResponseText
                });

                ExceptionInfo externalExceptionInfo = JsonExtension.TryConvertJsonToObject <ExceptionInfo>(httpEx.ExceptionReference.ResponseText);

                exception = httpEx.Handle(new { httpMethod, resourceName, resourceAction, key, queryString, externalExceptionInfo });
                //Reset key to new one so that in log system, this exception can be identified correctly.
                exception.ResetNewKey();
                throw exception;
            }
            catch (Exception ex)
            {
                exception = ex.Handle(new { httpMethod, resourceName, resourceAction, key, queryString });
                throw exception;
            }
            finally
            {
                ApiTraceContext.Exit(exception?.Key);
            }
        }
        /// <summary>
        /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler" /> interface.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpContext" /> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param>
        public void ProcessHttpApiContextContainer(HttpApiContextContainer <TRequest, TResponse> context)
        {
            context.EntryStamp = DateTime.UtcNow;
            context.SetResponseHeader(HttpConstants.HttpHeader.SERVERENTRYTIME, context.EntryStamp.ToFullDateTimeTzString());

            var acceptEncoding = context.TryGetRequestHeader(HttpConstants.HttpHeader.AcceptEncoding);

            try
            {
                //First of all, clean thread info for context.
                ContextHelper.Reinitialize();

                context.TraceId       = context.TryGetRequestHeader(HttpConstants.HttpHeader.TRACEID);
                context.TraceSequence = context.TryGetRequestHeader(HttpConstants.HttpHeader.TRACESEQUENCE).ToNullableInt32();

                Prepare(context.Request);

                if (context.HttpMethod.Equals(HttpConstants.HttpMethod.Options, StringComparison.OrdinalIgnoreCase))
                {
                    if (AllowOptions)
                    {
                        //Return directly. IIS would append following headers by default, according to what exactly web.config have.
                        //Access-Control-Allow-Origin: *
                        //Access-Control-Allow-Headers: Content-Type
                        //Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS

                        context.SetResponseHeader(HttpConstants.HttpHeader.SERVEREXITTIME, DateTime.UtcNow.ToFullDateTimeTzString());

                        return;
                    }
                }

                // Authentication has already done inside ProcessRoute.
                var runtimeContext = ProcessRoute(context);
                runtimeContext.CheckNullObject(nameof(runtimeContext));
                context.RuntimeContext = runtimeContext;

                // Fill basic context info.

                var userAgentHeaderKey = context.Settings?.OriginalUserAgentHeaderKey;

                //ContextHelper.ConsistContext(
                //    // TOKEN
                //    context.TryGetRequestHeader((context.Settings?.TokenHeaderKey).SafeToString(HttpConstants.HttpHeader.TOKEN)),
                //    // Settings
                //    context.Settings,
                //    // IP Address
                //    context.TryGetRequestHeader((context.Settings?.OriginalIpAddressHeaderKey).SafeToString(HttpConstants.HttpHeader.ORIGINAL)).SafeToString(context.ClientIpAddress),
                //    // User Agent
                //    string.IsNullOrWhiteSpace(userAgentHeaderKey) ? context.UserAgent : context.TryGetRequestHeader(userAgentHeaderKey).SafeToString(context.UserAgent),
                //    // Culture Code
                //    context.QueryString.Get(HttpConstants.QueryString.Language).SafeToString(context.UserLanguages.SafeFirstOrDefault()).EnsureCultureCode(),
                //     // Current Uri
                //     context.Url,
                //     HttpExtension.GetBasicAuthentication(context.TryGetRequestHeader(HttpConstants.HttpHeader.Authorization).DecodeBase64()),
                //     runtimeContext.ApiRouterIdentifier
                //    );

                if (runtimeContext.OperationParameters?.EntitySynchronizationMode != null)
                {
                    ContextHelper.ApiContext.LastSynchronizedStamp = context.TryGetRequestHeader(runtimeContext.OperationParameters.EntitySynchronizationMode.IfModifiedSinceKey).ObjectToDateTime();
                }

                // Fill finished.
                if (string.IsNullOrWhiteSpace(runtimeContext.Realm) && runtimeContext.Version.Equals(ApiConstants.BuiltInFeatureVersionKeyword, StringComparison.OrdinalIgnoreCase))
                {
                    string contentType;
                    var    buildInResult = ProcessBuiltInFeature(context, runtimeContext, context.IsLocal, out contentType);
                    PackageResponse(context, buildInResult, new RuntimeApiOperationParameters {
                        ContentType = contentType
                    }, null, acceptEncoding, runtimeContext.IsVoid ?? false, context.Settings);
                }
                else
                {
                    //Initialize additional header keys
                    if ((runtimeContext.OperationParameters?.CustomizedHeaderKeys).HasItem())
                    {
                        var currentApiContext = ContextHelper.ApiContext;

                        foreach (var one in runtimeContext.OperationParameters.CustomizedHeaderKeys)
                        {
                            currentApiContext.CustomizedHeaders.Merge(one, context.TryGetRequestHeader(one));
                        }
                    }

                    InitializeContext(context.Request, runtimeContext);

                    byte[] body = context.ReadRequestBody();
                    context.CollectTrackingInfo(body.LongLength);

                    if (runtimeContext.Exception != null)
                    {
                        throw runtimeContext.Exception;
                    }

                    ApiTraceContext.Enter(runtimeContext, setNameAsMajor: true);
                    string jsonBody = null;

                    try
                    {
                        if (runtimeContext.ApiCacheStatus == ApiCacheStatus.UseCache)
                        {
                            jsonBody = runtimeContext.CachedResponseBody;
                        }
                        else
                        {
                            var invokeResult = Invoke(runtimeContext.ApiInstance, runtimeContext.ApiMethod, context, runtimeContext.EntityKey, out jsonBody);

                            if (runtimeContext.ApiCacheStatus == ApiCacheStatus.UpdateCache)
                            {
                                runtimeContext.ApiCacheContainer.Update(runtimeContext.ApiCacheIdentity, jsonBody);
                            }

                            PackageResponse(context, invokeResult, runtimeContext.OperationParameters, null, acceptEncoding, runtimeContext.IsVoid ?? false, settings: context.Settings);
                        }
                    }
                    catch (Exception invokeEx)
                    {
                        context.BaseException = invokeEx.Handle(new { Url = context.RawUrl, Method = context.HttpMethod });
                        throw context.BaseException;
                    }
                    finally
                    {
                        ApiTraceContext.Exit(context.BaseException?.Key);
                    }
                }
            }
            catch (Exception ex)
            {
                var apiTracking = context.Settings?.ApiTracking;
                context.BaseException = (ex as BaseException) ?? ex.Handle(new { Uri = context.Url.SafeToString() });

                (apiTracking ?? Framework.ApiTracking)?.LogException(context.BaseException.ToExceptionInfo(eventKey: context.ApiEvent?.Key.SafeToString()));

                if (context.ApiEvent != null)
                {
                    context.ApiEvent.ExceptionKey = context.BaseException.Key;
                }

                PackageResponse(context, null, null, context.BaseException, acceptEncoding, settings: context.Settings);
            }
            finally
            {
                if (context.Settings?.ApiTracking != null)
                {
                    var exitStamp = DateTime.UtcNow;
                    if (context.ApiEvent != null)
                    {
                        try
                        {
                            context.ApiEvent.ExitStamp = exitStamp;
                            context.Settings.ApiTracking.LogApiEvent(context.ApiEvent);
                        }
                        catch { }
                    }

                    if (ApiTraceContext.Root != null)
                    {
                        try
                        {
                            ApiTraceContext.Exit(context.BaseException?.Key, exitStamp);
                            context.Settings.ApiTracking.LogApiTraceLog(ApiTraceContext.GetCurrentTraceLog(true));
                        }
                        catch { }
                    }
                }

                Dispose();
            }
        }
        /// <summary>
        /// Called by the ASP.NET MVC framework before the action method executes.
        /// </summary>
        /// <param name="filterContext">The filter context.</param>
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);

            IsOptions = HandleOptionsRequests(filterContext);

            if (Depth < 1)
            {
                Depth = 0;
                ContextHelper.Reinitialize();

                DateTime entryStamp = DateTime.UtcNow;
                var      request    = filterContext.HttpContext.Request;

                var traceId       = request.TryGetHeader(HttpConstants.HttpHeader.TRACEID);
                var traceSequence = request.TryGetHeader(HttpConstants.HttpHeader.TRACESEQUENCE).ObjectToNullableInt32();
                var methodInfo    = (filterContext.ActionDescriptor as ReflectedActionDescriptor)?.MethodInfo;

                var httpContextContainer = new HttpBaseApiContextContainer(request, filterContext.HttpContext.Response);
                ContextHelper.ConsistContext(httpContextContainer, settings);

                WebThreadPool.Register();

                if (!string.IsNullOrWhiteSpace(traceId))
                {
                    ApiTraceContext.Initialize(traceId, traceSequence, entryStamp);
                }

                if (settings != null && settings.TrackingEvent)
                {
                    var context = filterContext.HttpContext;
                    ApiEvent = new ApiEventLog
                    {
                        RawUrl     = context.Request.RawUrl,
                        EntryStamp = entryStamp,
                        TraceId    = traceId,
                        // If request came from ApiTransport or other proxy ways, ORIGINAL stands for the IP ADDRESS from original requester.
                        IpAddress          = context.Request.TryGetHeader(settings?.OriginalIpAddressHeaderKey.SafeToString(HttpConstants.HttpHeader.ORIGINAL)).SafeToString(context.Request.UserHostAddress),
                        CultureCode        = ContextHelper.ApiContext.CultureCode,
                        ContentLength      = context.Request.ContentLength,
                        OperatorCredential = ContextHelper.CurrentCredential as BaseCredential,
                        ServerIdentifier   = EnvironmentCore.MachineName,
                        ServiceIdentifier  = EnvironmentCore.ProductName
                    };
                }

                var controllerType = methodInfo?.DeclaringType;

                var tokenRequiredAttribute = methodInfo?.GetCustomAttribute <TokenRequiredAttribute>(true) ?? controllerType?.GetCustomAttribute <TokenRequiredAttribute>(true);
                var permissionAttributes   = controllerType?.GetCustomAttributes <ApiPermissionAttribute>(true).ToDictionary();
                permissionAttributes.Merge(methodInfo?.GetCustomAttributes <ApiPermissionAttribute>(true).ToDictionary(), true);

                var tokenRequired = tokenRequiredAttribute != null && tokenRequiredAttribute.TokenRequired;

                if (tokenRequired)
                {
                    if (ContextHelper.CurrentCredential == null)
                    {
                        var baseException = (new UnauthorizedTokenException(ContextHelper.Token)).Handle(
                            filterContext.HttpContext.Request.ToExceptionScene(filterContext.RouteData?.GetControllerName()), data: new { filterContext.HttpContext.Request.RawUrl });

                        HandleUnauthorizedAction(filterContext, baseException);
                    }
                    else if (permissionAttributes.HasItem())
                    {
                        var baseException = ContextHelper.CurrentUserInfo?.Permissions.ValidateApiPermission(permissionAttributes, ContextHelper.Token, methodInfo?.GetFullName());

                        if (baseException != null)
                        {
                            HandleUnauthorizedAction(filterContext, baseException);
                        }
                    }
                }

                ApiTraceContext.Enter(methodInfo.GetFullName(), setNameAsMajor: true);
            }

            Depth++;
        }
        /// <summary>
        /// Called when [result executed].
        /// </summary>
        /// <param name="filterContext">The filter context.</param>
        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            base.OnResultExecuted(filterContext);

            if (!IsOptions)
            {
                // If using BeyovaBaseController, exception should be logger there, and in this method, no exception should appear.
                var baseException = filterContext.Exception?.Handle(new ExceptionScene
                {
                    MethodName = string.Format("{0}: {1}", filterContext.HttpContext.Request.HttpMethod, filterContext.HttpContext.Request.RawUrl)
                }, data: (filterContext.Exception as BaseException)?.ReferenceData);

                if (baseException != null)
                {
                    filterContext.Exception = baseException;
                }

                if (Depth < 2)
                {
                    if (ApiTracking != null)
                    {
                        DateTime exitStamp = DateTime.UtcNow;

                        // API EXCEPTION
                        if (baseException != null)
                        {
                            try
                            {
                                ApiTracking.LogException(baseException.ToExceptionInfo());
                            }
                            catch { }
                        }

                        // API EVENT
                        if (ApiEvent != null)
                        {
                            try
                            {
                                ApiEvent.ExitStamp    = exitStamp;
                                ApiEvent.ExceptionKey = baseException?.Key;

                                ApiTracking.LogApiEvent(ApiEvent);
                            }
                            catch { }
                        }

                        // API TRACE
                        try
                        {
                            ApiTraceContext.Exit((ApiEvent?.ExceptionKey) ?? (baseException?.Key), exitStamp);
                            var traceLog = ApiTraceContext.GetCurrentTraceLog(true);

                            if (traceLog != null)
                            {
                                ApiTracking.LogApiTraceLog(traceLog);
                            }
                        }
                        catch { }
                    }

                    ContextHelper.Clear();
                }

                Depth--;
            }
        }