/// <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); } }
/// <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--; } }