/// <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> /// Invokes the specified method information. /// <remarks> /// Invoke action would regard to method parameter to use difference logic. Following steps show the IF-ELSE case. When it is hit, other would not go through. /// <list type="number"><item> /// If input parameter count is 0, invoke without parameter object. /// </item><item> /// If input parameter count is 1 and key is not empty or null, invoke using key. /// </item><item> /// If input parameter count is 1 and key is empty or null, invoke using key, try to get JSON object from request body and convert to object for invoke. /// </item><item> /// If input parameter count more than 1, try read JSON data to match parameters by name (ignore case) in root level, then invoke. /// </item></list></remarks></summary> protected virtual object Invoke(object instance, MethodInfo methodInfo, HttpApiContextContainer <TRequest, TResponse> context, string key, out string jsonBody) { return(InternalInvoke(instance, methodInfo, context.ReadRequestBody(), context.Url, key, out jsonBody)); }