コード例 #1
0
        /// <summary>
        /// Handles client requests and routes to GET or POST methods accordingly
        /// </summary>
        /// <param name="context"></param>
        public void ProcessRequest(HttpContext context)
        {
#if DEBUG
            System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
            stopWatch.Start();
            logTime.Info("Start " + System.Reflection.MethodBase.GetCurrentMethod().Name);
#endif
            try
            {
                string         cacheKey = ""; // Key to request stored in cache
                ResponseBucket cacheResponse; // Request stored in cache
                string         postData = ""; // Data part of POST request (contains JSON query)

                // Negotiate with the request limiter (if enabled)
                if (_requestLimiter != null)
                {
                    if (!_requestLimiter.ClientLimitOK(context.Request))
                    {
                        // Deny request
                        // see 409 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
                        context.Response.AppendHeader("Retry-After", _requestLimiter.LimiterTimeSpan.ToString());
                        throw new HttpException(429, "429 - Too many requests in too short timeframe. Please try again later.");
                    }
                }


                if (Settings.Current.EnableCORS)
                {
                    // Enable CORS (Cross-Origin-Resource-Sharing)
                    context.Response.AppendHeader("Access-Control-Allow-Origin", "*");

                    // Enable Preflight requests
                    if (context.Request.HttpMethod == "OPTIONS")
                    {
                        context.Response.AppendHeader("Access-Control-Allow-Methods", "GET, POST");
                        context.Response.AppendHeader("Access-Control-Allow-Headers", "Content-Type");
                        //context.Response.End();
                        return;
                    }
                }


                // Create cache key by combining URL and query strings
                if (context.Request.HttpMethod == "POST")
                {
                    using (var stream = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
                    {
                        postData = stream.ReadToEnd();
                    }
                }


                cacheKey = ApiCache.CreateKey(context.Request.Url.AbsoluteUri + postData);
                // Try to get request from cache
                cacheResponse = ApiCache.Current.Fetch(cacheKey);
                if (cacheResponse != null)
                {
                    if (cacheResponse.Url.Equals(context.Request.Url.AbsoluteUri) && cacheResponse.PostData.Equals(postData))
                    {
                        // Use cached object
                        context.Send(cacheResponse, false);
                        if (context.Request.HttpMethod == "POST")
                        {
                            _usageLogger.Info(String.Format("url={0}, type=data, caller={3}, cached=true, format={1}, matrix-size={2}", context.Request.RawUrl, cacheResponse.ContentType, "?", context.Request.UserHostAddress));
                        }
                        else
                        {
                            _usageLogger.Info(String.Format("url={0}, type=metadata, caller={1}, cached=true, format=?, matrix-size=?", context.Request.RawUrl, context.Request.UserHostAddress));
                        }
#if DEBUG
                        stopWatch.Stop();
                        logTime.InfoFormat(System.Reflection.MethodBase.GetCurrentMethod().Name + " Done from cahce in ms = {0}", stopWatch.ElapsedMilliseconds);
#endif
                        return;
                    }
                }
                if (_apiCacheLogger.IsDebugEnabled)
                {
                    _apiCacheLogger.DebugFormat("Key {0} not found in cache, creating response from backend.", cacheKey);
                }
                // Request object to be stored in cache
                cacheResponse          = new ResponseBucket();
                cacheResponse.Key      = cacheKey;
                cacheResponse.Url      = context.Request.Url.AbsoluteUri;
                cacheResponse.PostData = postData;

                List <string> routeParts = null;
                string        language   = null;
                string        db         = null;

                // Fetch the route data
                var routeData = context.Items["RouteData"] as RouteData;
                if (routeData.Values["language"] != null)
                {
                    language = routeData.Values["language"].ToString();
                }

                if (routeData.Values["path"] != null)
                {
                    routeParts = routeData.Values["path"].ToString().Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries).ToList();
                    db         = routeParts.First();
                    routeParts.RemoveAt(0);
                }
                // Parse query string
                var options = new Options(context.Request.QueryString);

                var queryKeys = context.Request.QueryString.ToString().Split('&');

                if (queryKeys.Contains("config"))
                {
                    var obj = new {
                        maxValues  = Settings.Current.MaxValues,
                        maxCells   = Settings.Current.FetchCellLimit,
                        maxCalls   = Settings.Current.LimiterRequests,
                        timeWindow = Settings.Current.LimiterTimeSpan,
                        CORS       = Settings.Current.EnableCORS
                    };

                    cacheResponse.ContentType  = "application/json; charset=" + System.Text.Encoding.UTF8.WebName;
                    cacheResponse.ResponseData = context.Response.ContentEncoding.GetBytes(obj.ToJSON(options.PrettyPrint));
                    context.Send(cacheResponse, true);
                    _usageLogger.Info(String.Format("url={0}, type=config, caller={1}, cached=false", context.Request.RawUrl, context.Request.UserHostAddress));

#if DEBUG
                    stopWatch.Stop();
                    logTime.InfoFormat(System.Reflection.MethodBase.GetCurrentMethod().Name + " Done in ms = {0}", stopWatch.ElapsedMilliseconds);
#endif
                    return;
                }

                if (ExposedDatabases.DatabaseConfigurations.ContainsKey(language) == false)
                {
                    throw new ArgumentException("The specified language '" + language + "' does not exist");
                }
                else if (db == null)
                {
                    var databases = new List <MetaDb>();
                    foreach (var database in ExposedDatabases.DatabaseConfigurations[language].Keys)
                    {
                        databases.Add(new MetaDb
                        {
                            Id   = database,
                            Text = ExposedDatabases.DatabaseConfigurations[language][database].Name
                        });
                    }
                    if (databases.Count > 0)
                    {
                        cacheResponse.ContentType  = "application/json; charset=" + System.Text.Encoding.UTF8.WebName;
                        cacheResponse.ResponseData = context.Response.ContentEncoding.GetBytes(databases.ToJSON(options.PrettyPrint));
                        context.Send(cacheResponse, true);
                        //context.SendJSON(databases.ToJSON(options.PrettyPrint));
                    }
                    else
                    {
                        throw new ArgumentException("No databases found in the specified language");
                    }
                }
                else // We have a database specified
                {
                    if (ExposedDatabases.DatabaseConfigurations[language].ContainsKey(db) == false)
                    {
                        // Database does not exist
                        throw new ArgumentException("The specified database '" + db + "' does not exist");
                    }
                    else
                    {
                        // Try to get a menu item first
                        string[] strNodePath = (routeParts == null || routeParts.Count == 0) ? new string[] { "" } : routeParts.ToArray();


                        var menu = PCAxisRepository.GetMenu(db, language, strNodePath);
                        if (menu == null)
                        {
                            //TODO Check exception type
                            throw new Exception("Error while connecting to data source");
                        }
                        else if ((menu.ID.Menu == "") && (menu.ID.Selection == ""))
                        {
                            // Could not find level in tree - Check if it is a short URL with table
                            if (strNodePath.Length == 1 && strNodePath[0] != "")
                            {
                                var possibleNameOrId = strNodePath[0];
                                PCAxis.Search.SearchStatusType status;
                                var text    = string.Format("{0}:{2} OR {1}:{2}", PCAxis.Search.SearchConstants.SEARCH_FIELD_SEARCHID, PCAxis.Search.SearchConstants.SEARCH_FIELD_TABLEID, possibleNameOrId);
                                var results = PCAxis.Search.SearchManager.Current.Search(db, language, text, out status);

                                if (status == Search.SearchStatusType.Successful && results.Count > 0)
                                {
                                    if (_logger.IsDebugEnabled)
                                    {
                                        if (results.Count > 1)
                                        {
                                            for (int cnter = 1; cnter < results.Count; cnter++)
                                            {
                                                if (!String.Equals(results[cnter - 1].Table, results[cnter].Table))
                                                {
                                                    _logger.DebugFormat("Hmmm, not some result have different table names: {0}  differs from  {1}", results[cnter - 1].Table, results[cnter].Table);
                                                }
                                            }
                                        }
                                    }

                                    strNodePath = (results[0].Path + "/" + results[0].Table).Split('/');
                                    //Remove first element in array
                                    strNodePath = strNodePath.Where((source, index) => index != 0).ToArray();
                                    routeParts  = strNodePath.ToList();

                                    // Get the menu again...
                                    menu = PCAxisRepository.GetMenu(db, language, strNodePath);
                                }
                            }
                        }
                        //else
                        //{
                        var currentItem = menu;    //.CurrentItem as Item;

                        if (currentItem.MenuType == typeof(PxMenuItem) && currentItem.HasSubItems)
                        {
                            if (!string.IsNullOrEmpty(options.SearchQuery))
                            {
                                // Find tables using the search function
                                PCAxis.Search.SearchStatusType status;

                                cacheResponse.ContentType  = "application/json; charset=" + System.Text.Encoding.UTF8.WebName;
                                cacheResponse.ResponseData = context.Response.ContentEncoding.GetBytes(GetSearchResult(db, language, options.SearchQuery, options.SearchFilter, routeParts, out status).ToJSON(options.PrettyPrint));

                                if (status == Search.SearchStatusType.Successful)
                                {
                                    context.Send(cacheResponse, false);     // Send without caching
                                }
                                else
                                {
                                    // Trying to search a non-indexed database...
                                    context.SendJSONError(Error("Search not activated", false), 400);
                                    _usageLogger.Info(String.Format("url={0}, type=error, caller={1}, cached=false, message=Search not activated", context.Request.RawUrl, context.Request.UserHostAddress));
                                    _logger.Warn(String.Format("Search not activated for {0} - {1}", db, language));
#if DEBUG
                                    stopWatch.Stop();
                                    logTime.InfoFormat(System.Reflection.MethodBase.GetCurrentMethod().Name + " Done in ms = {0}", stopWatch.ElapsedMilliseconds);
#endif
                                    return;
                                }
                            }
                            else
                            {
                                // Current item is a list item - return meta data for this list
                                cacheResponse.ContentType  = "application/json; charset=" + System.Text.Encoding.UTF8.WebName;
                                cacheResponse.ResponseData = context.Response.ContentEncoding.GetBytes(currentItem.MetaList.ToJSON(options.PrettyPrint));
                                context.Send(cacheResponse, true);
                            }

                            //context.SendJSON(GetMetaList(context, (PxMenuItem)currentItem).ToJSON(options.PrettyPrint));
                        }
                        else if (context.Request.HttpMethod == "GET" && currentItem.MenuType == typeof(TableLink))
                        {
                            // Current item is not a list. Try to return table meta data
                            cacheResponse.ContentType  = "application/json; charset=" + System.Text.Encoding.UTF8.WebName;
                            cacheResponse.ResponseData = context.Response.ContentEncoding.GetBytes(GetTableMeta(context, language, db, strNodePath).ToJSON(options.PrettyPrint));

                            context.Send(cacheResponse, true);

                            // Logs usage
                            _usageLogger.Info(String.Format("url={0}, type=metadata, caller={1}, cached=false, format=json", context.Request.RawUrl, context.Request.UserHostAddress));
                        }
                        else if (context.Request.HttpMethod == "POST" && currentItem.MenuType == typeof(TableLink))
                        {
                            // Current item is not a list. Try to return table data.
                            SendTableData(context, language, db, routeParts.ToArray(), cacheResponse);
                        }
                        else
                        {
                            context.SendJSONError(Error("Parameter error", false), 400);
                            // Logs usage
                            _usageLogger.Info(String.Format("url={0}, type=error, caller={1}, cached=false, message=Parameter error", context.Request.RawUrl, context.Request.UserHostAddress));
                        }
                        //}
                    }
                }
#if DEBUG
                stopWatch.Stop();
                logTime.InfoFormat(System.Reflection.MethodBase.GetCurrentMethod().Name + " Done in ms = {0}", stopWatch.ElapsedMilliseconds);
#endif
            }
            catch (HttpException ex)
            {
#if DEBUG
                stopWatch.Stop();
                logTime.InfoFormat(System.Reflection.MethodBase.GetCurrentMethod().Name + " Error Done in ms = {0}", stopWatch.ElapsedMilliseconds);
#endif
                if (ex.GetHttpCode() == 429)
                {
                    context.Response.TrySkipIisCustomErrors = true;
                    context.SendJSONError(Error(ex.Message, false), 429);
                }
                else
                {
                    context.SendJSONError(Error("Parameter error", false), ex.GetHttpCode());
                }
                // Logs usage
                _usageLogger.Info(String.Format("url={0}, type=error, caller={1}, cached=false", context.Request.RawUrl, context.Request.UserHostAddress), ex);
                _logger.Warn(ex);
            }
            catch (Exception ex)
            {
                //if (context.Request.HttpMethod != "OPTIONS")
                //{

#if DEBUG
                stopWatch.Stop();
                logTime.InfoFormat(System.Reflection.MethodBase.GetCurrentMethod().Name + " Error Done in ms = {0}", stopWatch.ElapsedMilliseconds);
#endif
                try
                {
                    context.SendJSONError(Error("Parameter error", false), 400);
                }
                catch (Exception sendEx)
                {
                    _logger.Error(String.Format("url={0}, type=error, caller={1}", context.Request.RawUrl, context.Request.UserHostAddress), sendEx);
                }
                _usageLogger.Info(String.Format("url={0}, type=error, caller={1}, cached=false ", context.Request.RawUrl, context.Request.UserHostAddress), ex);
                _logger.Warn(ex);
                //}
            }
        }