예제 #1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="RuntimeRoute" /> class.
        /// </summary>
        /// <param name="routeIdentifier">The route key.</param>
        /// <param name="methodInfo">The method information.</param>
        /// <param name="instanceType">Type of the instance.</param>
        /// <param name="instance">The instance.</param>
        /// <param name="isActionUsed">if set to <c>true</c> [is action used].</param>
        /// <param name="isTokenRequired">if set to <c>true</c> [is token required].</param>
        /// <param name="moduleName">Name of the module.</param>
        /// <param name="contentType">Type of the content.</param>
        /// <param name="setting">The setting.</param>
        /// <param name="apiCacheAttribute">The API cache attribute.</param>
        /// <param name="omitApiTracking">The omit API tracking.</param>
        /// <param name="permissions">The permissions.</param>
        /// <param name="headerKeys">The header keys.</param>
        public RuntimeRoute(ApiRouteIdentifier routeIdentifier, MethodInfo methodInfo, Type instanceType, object instance, bool isActionUsed, bool isTokenRequired, string moduleName, string contentType, RestApiSettings setting, ApiCacheAttribute apiCacheAttribute, OmitApiTrackingAttribute omitApiTracking, IDictionary <string, ApiPermissionAttribute> permissions = null, List <string> headerKeys = null) : this()
        {
            ApiMethod       = methodInfo;
            ApiInstance     = instance;
            IsActionUsed    = isActionUsed;
            InstanceType    = instanceType;
            Setting         = setting;
            OmitApiTracking = omitApiTracking;

            OperationParameters = new RuntimeApiOperationParameters
            {
                ContentType          = contentType,
                IsTokenRequired      = isTokenRequired,
                CustomizedHeaderKeys = headerKeys,
                Permissions          = permissions,
                ModuleName           = moduleName
            };

            ApiRouteIdentifier = routeIdentifier;
            IsVoid             = ApiMethod?.ReturnType?.IsVoid();

            if (apiCacheAttribute != null)
            {
                ApiCacheAttribute = apiCacheAttribute;
                ApiCacheContainer = apiCacheAttribute.CacheContainer ?? new ApiCacheContainer(routeIdentifier.ToString(), apiCacheAttribute.CacheParameter);
            }
        }
예제 #2
0
        /// <summary>
        /// Initializes the routes.
        /// </summary>
        /// <param name="instance">The instance.</param>
        /// <param name="settings">The settings.</param>
        /// <exception cref="DataConflictException">Route</exception>
        private static void InitializeRoute(object instance, RestApiSettings settings = null)
        {
            lock (routeOperationLocker)
            {
                if (instance != null)
                {
                    var typeName = instance.GetType().FullName;
                    if (!initializedTypes.Contains(typeName))
                    {
                        #region Initialize routes

                        var doneInterfaceTypes = new List <string>();

                        foreach (var interfaceType in instance.GetType().GetInterfaces())
                        {
                            InitializeApiType(doneInterfaceTypes, routes, interfaceType, instance, settings);
                        }

                        #endregion Initialize routes

                        initializedTypes.Add(typeName);
                    }
                }
            }
        }
예제 #3
0
 /// <summary>
 /// Initializes a new instance of the <see cref="RestApiRouter" /> class.
 /// </summary>
 /// <param name="defaultApiSettings">The default API settings.</param>
 /// <param name="allowOptions">if set to <c>true</c> [allow options].</param>
 public RestApiRouter(RestApiSettings defaultApiSettings, bool allowOptions = false)
     : base(defaultApiSettings, allowOptions)
 {
     if (_firstInstance == null)
     {
         _firstInstance = this;
     }
 }
예제 #4
0
 /// <summary>
 /// Adds the handler (instance and settings) into route.
 /// </summary>
 /// <param name="instance">The instance.</param>
 /// <param name="settings">The settings.</param>
 public static void Add(object instance, RestApiSettings settings = null)
 {
     InitializeRoute(instance, settings);
 }
예제 #5
0
        /// <summary>
        /// Initializes the type of the API.
        /// </summary>
        /// <param name="doneInterfaceTypes">The done interface types.</param>
        /// <param name="routes">The routes.</param>
        /// <param name="interfaceType">Type of the interface.</param>
        /// <param name="instance">The instance.</param>
        /// <param name="settings">The settings.</param>
        /// <param name="parentApiContractAttribute">The parent API class attribute.</param>
        /// <param name="parentApiModuleAttribute">The parent API module attribute.</param>
        /// <param name="omitApiTrackingAttribute">The omit API tracking attribute.</param>
        /// <param name="parentTokenRequiredAttribute">The parent token required attribute.</param>
        /// <exception cref="DataConflictException">routeKey</exception>
        private static void InitializeApiType(List <string> doneInterfaceTypes, Dictionary <ApiRouteIdentifier, RuntimeRoute> routes, Type interfaceType, object instance, RestApiSettings settings = null, ApiContractAttribute parentApiContractAttribute = null, ApiModuleAttribute parentApiModuleAttribute = null, OmitApiTrackingAttribute omitApiTrackingAttribute = null, TokenRequiredAttribute parentTokenRequiredAttribute = null)
        {
            if (routes != null && interfaceType != null && doneInterfaceTypes != null)
            {
                if (doneInterfaceTypes.Contains(interfaceType.FullName))
                {
                    return;
                }

                var apiContract            = parentApiContractAttribute ?? interfaceType.GetCustomAttribute <ApiContractAttribute>(true);
                var omitApiTracking        = omitApiTrackingAttribute ?? interfaceType.GetCustomAttribute <OmitApiTrackingAttribute>(true);
                var apiModule              = parentApiModuleAttribute ?? interfaceType.GetCustomAttribute <ApiModuleAttribute>(true);
                var tokenRequiredAttribute = parentTokenRequiredAttribute ?? interfaceType.GetCustomAttribute <TokenRequiredAttribute>(true);
                var moduleName             = apiModule?.ToString();

                if (apiContract != null && !string.IsNullOrWhiteSpace(apiContract.Version))
                {
                    if (apiContract.Version.SafeEquals(ApiConstants.BuiltInFeatureVersionKeyword, StringComparison.OrdinalIgnoreCase))
                    {
                        throw ExceptionFactory.CreateInvalidObjectException(nameof(apiContract.Version), reason: "<builtin> cannot be used as version due to it is used internally.");
                    }

                    foreach (var method in interfaceType.GetMethods())
                    {
                        var apiOperationAttribute = method.GetCustomAttribute <ApiOperationAttribute>(true);

                        #region Initialize based on ApiOperation

                        if (apiOperationAttribute != null)
                        {
                            var permissions          = new Dictionary <string, ApiPermissionAttribute>();
                            var additionalHeaderKeys = new HashSet <string>();

                            var apiPermissionAttributes =
                                method.GetCustomAttributes <ApiPermissionAttribute>(true);

                            var apiCacheAttribute = method.GetCustomAttribute <ApiCacheAttribute>(true);

                            if (apiPermissionAttributes != null)
                            {
                                foreach (var one in apiPermissionAttributes)
                                {
                                    permissions.Merge(one.PermissionIdentifier, one);
                                }
                            }

                            var headerKeyAttributes = method.GetCustomAttributes <ApiHeaderAttribute>(true);
                            if (headerKeyAttributes != null)
                            {
                                foreach (var one in headerKeyAttributes)
                                {
                                    additionalHeaderKeys.Add(one.HeaderKey);
                                }
                            }

                            var routeKey = ApiRouteIdentifier.FromApiObjects(apiContract, apiOperationAttribute);

                            var tokenRequired =
                                method.GetCustomAttribute <TokenRequiredAttribute>(true) ??
                                tokenRequiredAttribute;

                            // If method can not support API cache, consider as no api cache.
                            if (apiCacheAttribute != null && (!apiOperationAttribute.HttpMethod.Equals(HttpConstants.HttpMethod.Get, StringComparison.OrdinalIgnoreCase) || !apiCacheAttribute.InitializeParameterNames(method)))
                            {
                                apiCacheAttribute = null;
                            }

                            var runtimeRoute = new RuntimeRoute(routeKey, method, interfaceType, instance,
                                                                !string.IsNullOrWhiteSpace(apiOperationAttribute.Action),
                                                                tokenRequired != null && tokenRequired.TokenRequired, moduleName, apiOperationAttribute.ContentType, settings, apiCacheAttribute, omitApiTracking ?? method.GetCustomAttribute <OmitApiTrackingAttribute>(true), permissions, additionalHeaderKeys.ToList());

                            if (routes.ContainsKey(routeKey))
                            {
                                throw new DataConflictException(nameof(routeKey), objectIdentity: routeKey?.ToString(), data: new
                                {
                                    existed      = routes[routeKey].SafeToString(),
                                    newMethod    = method.GetFullName(),
                                    newInterface = interfaceType.FullName
                                });
                            }

                            // EntitySynchronizationModeAttribute
                            var entitySynchronizationModeAttribute = method.GetCustomAttribute <EntitySynchronizationModeAttribute>(true);
                            if (entitySynchronizationModeAttribute != null)
                            {
                                if (EntitySynchronizationModeAttribute.IsReturnTypeMatched(method.ReturnType))
                                {
                                    runtimeRoute.OperationParameters.EntitySynchronizationMode = entitySynchronizationModeAttribute;
                                }
                            }

                            routes.Add(routeKey, runtimeRoute);
                        }

                        #endregion Initialize based on ApiOperation
                    }

                    foreach (var one in interfaceType.GetInterfaces())
                    {
                        InitializeApiType(doneInterfaceTypes, routes, one, instance, settings, apiContract, apiModule, omitApiTracking, tokenRequiredAttribute);
                    }

                    //Special NOTE:
                    // Move this add action in scope of if apiContract is valid.
                    // Reason: in complicated cases, when [A:Interface1] without ApiContract, but [Interface2: Interface] with defining ApiContract, and [B: A, Interface2], then correct contract definition might be missed.
                    doneInterfaceTypes.Add(interfaceType.FullName);
                }
            }
        }
예제 #6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ApiHandlerBase" /> class.
 /// </summary>
 /// <param name="defaultApiSettings">The default API settings.</param>
 /// <param name="allowOptions">if set to <c>true</c> [allow options].</param>
 protected ApiHandlerBase(RestApiSettings defaultApiSettings, bool allowOptions = false)
 {
     RestApiSettingPool.AddSetting(defaultApiSettings);
     AllowOptions = allowOptions;
 }
예제 #7
0
        /// <summary>
        /// Packages the response.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="data">The data.</param>
        /// <param name="operationParameters">The operation parameters.</param>
        /// <param name="ex">The ex.</param>
        /// <param name="acceptEncoding">The accept encoding.</param>
        /// <param name="noBody">if set to <c>true</c> [no body].</param>
        /// <param name="settings">The settings.</param>
        public static void PackageResponse(HttpContextContainer <TRequest, TResponse> context, object data, RuntimeApiOperationParameters operationParameters = null, BaseException ex = null, string acceptEncoding = null, bool noBody = false, RestApiSettings settings = null)
        {
            if (context != null)
            {
                if (settings == null)
                {
                    settings = RestApiSettingPool.DefaultRestApiSettings;
                }

                var objectToReturn = ex != null ? (settings.OmitExceptionDetail ? ex.ToSimpleExceptionInfo() : ex.ToExceptionInfo()) : data;

                context.SetResponseHeader(HttpConstants.HttpHeader.SERVERNAME, EnvironmentCore.MachineName);
                context.SetResponseHeader(HttpConstants.HttpHeader.TRACEID, ApiTraceContext.TraceId);
                context.SetResponseHeader(HttpConstants.HttpHeader.POWEREDBY, HttpConstants.HttpValues.PoweredByBeyova);
                context.SetResponseHeader(HttpConstants.HttpHeader.BEYOVAAPIVERSION, BeyovaVersion.CommonVesion);

                int httpStatusCode = (int)(ex == null ? (noBody ? HttpStatusCode.NoContent : HttpStatusCode.OK) : ex.Code.ToHttpStatusCode());

                if (ex == null && operationParameters?.EntitySynchronizationMode != null)
                {
                    DateTime?lastModifiedStamp = null;
                    data = operationParameters.EntitySynchronizationMode.RebuildOutputObject(ContextHelper.ApiContext.LastSynchronizedStamp, data, ref httpStatusCode, ref noBody, out lastModifiedStamp);

                    if (lastModifiedStamp.HasValue)
                    {
                        context.SetResponseHeader(operationParameters.EntitySynchronizationMode.LastModifiedKey, lastModifiedStamp.Value.ToFullDateTimeTzString());
                    }
                }

                context.ResponseStatusCode = (HttpStatusCode)httpStatusCode;

                if (!noBody)
                {
                    var    contentType    = HttpConstants.ContentType.Json;
                    var    contentWritten = false;
                    byte[] responseBytes  = null;

                    if (ex == null)
                    {
                        if (!string.IsNullOrWhiteSpace(operationParameters?.ContentType))
                        {
                            contentType = operationParameters.ContentType;

                            if (objectToReturn != null && contentType.StartsWith("application/", StringComparison.OrdinalIgnoreCase) && objectToReturn.GetType() == typeof(byte[]))
                            {
                                responseBytes = (byte[])objectToReturn;
                            }
                        }
                    }

                    if (responseBytes == null)
                    {
                        responseBytes = Framework.DefaultTextEncoding.GetBytes(contentType.Equals(HttpConstants.ContentType.Json, StringComparison.OrdinalIgnoreCase) ? objectToReturn.ToJson(true, JsonConverters) : objectToReturn.SafeToString());
                    }

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

                    if (settings.EnableContentCompression)
                    {
                        acceptEncoding = acceptEncoding.SafeToString().ToLowerInvariant();

                        if (acceptEncoding.Contains(HttpConstants.HttpValues.GZip))
                        {
                            context.WriteResponseGzipBody(responseBytes, contentType);
                            contentWritten = true;
                        }
                        else if (acceptEncoding.Contains(HttpConstants.HttpValues.Deflate))
                        {
                            context.WriteResponseDeflateBody(responseBytes, contentType);
                            contentWritten = true;
                        }
                    }

                    if (!contentWritten)
                    {
                        //return  as string;
                        context.WriteResponseBody(responseBytes, contentType);
                    }
                }
                else
                {
                    context.SetResponseHeader(HttpConstants.HttpHeader.SERVEREXITTIME, DateTime.UtcNow.ToFullDateTimeTzString());
                }
            }
        }
예제 #8
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ApiHandlerBase" /> class.
 /// </summary>
 /// <param name="defaultApiSettings">The default API settings.</param>
 /// <param name="allowOptions">if set to <c>true</c> [allow options].</param>
 protected ApiHandlerBase(RestApiSettings defaultApiSettings, bool allowOptions = false) : base(defaultApiSettings, allowOptions)
 {
 }
예제 #9
0
 /// <summary>
 /// Initializes a new instance of the <see cref="RestApiRouter" /> class.
 /// </summary>
 /// <param name="defaultApiSettings">The default API settings.</param>
 /// <param name="allowOptions">if set to <c>true</c> [allow options].</param>
 public RestApiRouter(RestApiSettings defaultApiSettings, bool allowOptions = false)
     : base(defaultApiSettings, allowOptions)
 {
 }
 /// <summary>
 /// Adds the setting.
 /// </summary>
 /// <param name="setting">The setting.</param>
 /// <param name="overrideIfExists">if set to <c>true</c> [override if exists].</param>
 /// <returns></returns>
 public static bool AddSetting(RestApiSettings setting, bool overrideIfExists = false)
 {
     return((setting != null) ? settingsContainer.Merge(setting.Name.SafeToString(), setting, overrideIfExists) : false);
 }
예제 #11
0
 /// <summary>
 /// Adds the handler (instance and settings) into route.
 /// </summary>
 /// <param name="instance">The instance.</param>
 /// <param name="settings">The settings.</param>
 public void Add(object instance, RestApiSettings settings = null)
 {
     RestApiRoutePool.Add(instance, settings);
 }