Exemple #1
0
        public virtual async Task Run(HttpContext context)
        {
            context.Features.Get <IHttpMaxRequestBodySizeFeature>().MaxRequestBodySize = null;
            var      requestStart = DateTime.Now;
            DateTime requestEnd;
            TimeSpan tspan;
            var      path      = CleanPath(context.Request.Path.ToString());
            var      paths     = path.Split('/').ToArray();
            var      isApiCall = false;

            Server.requestCount++;
            if (paths[paths.Length - 1].IndexOf(".") > 0)
            {
                //do not process files, but instead return a 404 error
                context.Response.StatusCode = 404;
                return;
            }

            if (Server.environment == Server.Environment.development)
            {
                Console.WriteLine("{0} " + context.Request.Method + " {1}", DateTime.Now.ToString("hh:mm:ss"), path);

                //optionally, wipe Scaffold cache to enable developer updates to html files when Server is running
                ScaffoldCache.cache = new Dictionary <string, SerializedScaffold>();
            }

            //get parameters from request body
            var parameters  = HeaderParameters.GetParameters(context);
            var requestBody = "";

            if (parameters.ContainsKey("_request-body"))
            {
                //extract request body from parameters
                requestBody = parameters["_request-body"];
                parameters.Remove("_request-body");
            }

            if (paths.Length > 1 && Server.servicePaths.Contains(paths[0]) == true)
            {
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                //run a web service via ajax (e.g. /api/namespace/class/function) //////////////////////////////////////////////////////////////////////////////////////////////////////
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                //execute web service
                Server.apiRequestCount++;
                isApiCall = true;

                object[] paramVals;
                var      param = "";

                //load service class from URL path
                context.Response.StatusCode = 200;
                string className  = Server.nameSpace + ".Services." + paths[1].Replace("-", "").ReplaceOnlyAlphaNumeric(true, true, false);
                string methodName = paths.Length > 2 ? paths[2] : Server.defaultServiceMethod;
                if (paths.Length >= 4)
                {
                    //path also contains extra namespace path(s)
                    for (var x = 2; x < paths.Length - 1; x++)
                    {
                        //add extra namespaces
                        className += "." + paths[x].Replace("-", "").ReplaceOnlyAlphaNumeric(true, true, false);
                    }
                    //get method name at end of path
                    methodName = paths[paths.Length - 1].Replace("-", "").ReplaceOnlyAlphaNumeric(true, true, false);
                }

                //get service type
                Type type = null;

                //get instance of service class
                var service = routes.FromServiceRoutes(context, parameters, className.Replace(Server.nameSpace + ".Services.", "").ToLower());
                if (service == null)
                {
                    try
                    {
                        service = (Web.Service)Activator.CreateInstance(Type.GetType(className), new object[] { context, parameters });
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message + "\n" + ex.StackTrace);
                        context.Response.StatusCode = 400;
                        await context.Response.WriteAsync("service error");

                        return;
                    }
                }

                //check if service class was found
                type = service.GetType();
                if (type == null)
                {
                    context.Response.StatusCode = 404;
                    await context.Response.WriteAsync("service does not exist");

                    return;
                }

                //update service fields
                service.path        = path;
                service.requestBody = requestBody;

                //get class method from service type
                MethodInfo method = type.GetMethod(methodName);

                //check if method exists
                if (method == null)
                {
                    context.Response.StatusCode = 404;
                    await context.Response.WriteAsync("service method " + methodName + " does not exist");

                    return;
                }

                //try to cast params to correct types
                ParameterInfo[] methodParams = method.GetParameters();

                paramVals = new object[methodParams.Length];
                for (var x = 0; x < methodParams.Length; x++)
                {
                    //find correct key/value pair
                    param = "";
                    var methodParamName = methodParams[x].Name.ToLower();
                    var paramType       = methodParams[x].ParameterType;

                    foreach (var item in parameters)
                    {
                        if (item.Key == methodParamName)
                        {
                            param = item.Value;
                            break;
                        }
                    }

                    if (param == "")
                    {
                        //set default value for empty parameter
                        if (paramType == typeof(Int32))
                        {
                            param = "0";
                        }
                    }

                    //cast params to correct (supported) types
                    if (paramType.Name != "String")
                    {
                        if (int.TryParse(param, out int i) == true)
                        {
                            if (paramType.IsEnum == true)
                            {
                                //convert param value to enum
                                paramVals[x] = Enum.Parse(paramType, param);
                            }
                            else
                            {
                                //convert param value to matching method parameter number type
                                paramVals[x] = Convert.ChangeType(i, paramType);
                            }
                        }
                        else if (paramType.FullName.Contains("DateTime"))
                        {
                            //convert param value to DateTime
                            if (param == "")
                            {
                                paramVals[x] = null;
                            }
                            else
                            {
                                try
                                {
                                    paramVals[x] = DateTime.Parse(param);
                                }
                                catch (Exception) { }
                            }
                        }
                        else if (paramType.IsArray)
                        {
                            //convert param value to array (of T)
                            var arr = param.Replace("[", "").Replace("]", "").Replace("\r", "").Replace("\n", "").Split(",").Select(a => { return(a.Trim()); }).ToList();
                            if (paramType.FullName == "System.Int32[]")
                            {
                                //convert param values to int array
                                paramVals[x] = arr.Select(a => { return(int.Parse(a)); }).ToArray();
                            }
                            else
                            {
                                //convert param values to array (of matching method parameter type)
                                paramVals[x] = Convert.ChangeType(arr, paramType);
                            }
                        }
                        else if (paramType.Name.IndexOf("Dictionary") == 0)
                        {
                            //convert param value (JSON) to Dictionary
                            paramVals[x] = (Dictionary <string, string>)Serializer.ReadObject(param, typeof(Dictionary <string, string>));
                        }
                        else if (paramType.Name == "Boolean")
                        {
                            paramVals[x] = param.ToLower() == "true";
                        }
                        else
                        {
                            //convert param value to matching method parameter type
                            paramVals[x] = Serializer.ReadObject(param, paramType);
                        }
                    }
                    else
                    {
                        //matching method parameter type is string
                        paramVals[x] = param;
                    }
                }

                string result = null;
                try
                {
                    //execute service method
                    result = (string)method.Invoke(service, paramVals);
                }
                catch (Exception ex)
                {
                    throw ex.InnerException;
                }
                if (context.Response.StatusCode == 200)
                {
                    //only write response if there were no errors

                    if (context.Response.ContentType == null)
                    {
                        //set content response as JSON
                        context.Response.ContentType = "text/json";
                    }
                    context.Response.ContentLength = result.Length;
                    if (result != null)
                    {
                        await context.Response.WriteAsync(result);
                    }
                    else
                    {
                        await context.Response.WriteAsync("{}");
                    }
                    service.Unload();
                }
            }
            else
            {
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                //page request (initialize client-side application) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                Server.pageRequestCount++;

                //create instance of Controller class based on request URL path
                var html     = "";
                var newpaths = path.Split('?', 2)[0].Split('/');
                var page     = routes.FromControllerRoutes(context, parameters, newpaths[0].ToLower());

                if (page == null)
                {
                    //page is not part of any known routes, try getting page class manually
                    var  typeName = (Server.nameSpace + ".Controllers." + (newpaths[0] == "" ? Server.defaultController : newpaths[0].Capitalize().Replace("-", " ")).Replace(" ", ""));
                    Type type     = Type.GetType(typeName);
                    if (type == null)
                    {
                        throw new Exception("type " + typeName + " does not exist");
                    }
                    page = (Mvc.Controller)Activator.CreateInstance(type, new object[] { context, parameters });
                }

                if (page != null)
                {
                    //render page
                    try
                    {
                        page.path        = path;
                        page.requestBody = requestBody;
                        html             = page.Render(newpaths);
                    }
                    catch (Exception ex)
                    {
                        if (Server.environment == Server.Environment.development)
                        {
                            Console.WriteLine(ex.Message + "\n" + ex.StackTrace);
                        }
                        throw ex;
                    }
                }
                else
                {
                    //show 404 error
                    page = new Mvc.Controller(context, parameters);
                    html = page.Error404();
                }

                //unload Datasilk Core
                page.Unload();
                page = null;

                //send response back to client
                if (context.Response.ContentType == null ||
                    context.Response.ContentType == "")
                {
                    context.Response.ContentType = "text/html";
                }
                if (context.Response.HasStarted == false)
                {
                    await context.Response.WriteAsync(html);
                }
            }

            if (Server.environment == Server.Environment.development)
            {
                requestEnd          = DateTime.Now;
                tspan               = requestEnd - requestStart;
                Server.requestTime += (tspan.Seconds);
                Console.WriteLine("END REQUEST {0} ms, {1} {2}", tspan.Milliseconds, path, isApiCall ? "Service" : "Controller");
                Console.WriteLine("");
            }
        }
Exemple #2
0
        public virtual async void Run(HttpContext context)
        {
            context.Features.Get <IHttpMaxRequestBodySizeFeature>().MaxRequestBodySize = null;
            var      requestStart = DateTime.Now;
            DateTime requestEnd;
            TimeSpan tspan;
            var      requestType = "";
            var      path        = cleanPath(context.Request.Path.ToString());
            var      paths       = path.Split('/').ToArray();
            var      extension   = "";

            //get request file extension (if exists)
            if (path.IndexOf(".") >= 0)
            {
                for (int x = path.Length - 1; x >= 0; x += -1)
                {
                    if (path.Substring(x, 1) == ".")
                    {
                        extension = path.Substring(x + 1); return;
                    }
                }
            }

            server.requestCount += 1;

            if (server.environment == Server.enumEnvironment.development)
            {
                Console.WriteLine("--------------------------------------------");
                Console.WriteLine("{0} GET {1}", DateTime.Now.ToString("hh:mm:ss"), path);

                //optionally, wipe Scaffold cache to enable developer updates to html files when server is running
                server.Scaffold = new Dictionary <string, SerializedScaffold>();
            }

            //get form files (if any exist)
            IFormCollection form = null;

            if (context.Request.ContentType != null)
            {
                if (context.Request.ContentType.IndexOf("multipart/form-data") >= 0)
                {
                    form = context.Request.Form;
                }
            }

            byte[] bytes    = new byte[0];
            string data     = "";
            int    dataType = 0; //0 = ajax, 1 = HTML form post, 2 = multi-part form (with file uploads)

            //figure out what kind of data was sent with the request
            if (form == null && context.Request.Body.CanRead)
            {
                //get POST data from request
                using (MemoryStream ms = new MemoryStream())
                {
                    context.Request.Body.CopyTo(ms);
                    bytes = ms.ToArray();
                }
                data = Encoding.UTF8.GetString(bytes, 0, bytes.Length).Trim();
            }
            else
            {
                //form files exist
                dataType = 2;
            }

            if (paths.Length > 1)
            {
                if (paths[0] == "api")
                {
                    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                    //run a web service via ajax (e.g. /api/namespace/class/function) //////////////////////////////////////////////////////////////////////////////////////////////////////
                    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                    if (cleanNamespace(paths))
                    {
                        //execute web service
                        requestType = "service";
                        //get parameters from request body, including page id
                        var      parms = new Dictionary <string, string>();
                        object[] paramVals;
                        var      param = "";


                        if (data.Length > 0)
                        {
                            if (data.IndexOf("Content-Disposition") > 0)
                            {
                                //multi-part file upload
                                dataType = 2;
                            }
                            else if (data.IndexOf("{") >= 0 && data.IndexOf("}") > 0 && data.IndexOf(":") > 0)
                            {
                                //get method parameters from POST S.ajax.post()
                                Dictionary <string, object> attr = JsonConvert.DeserializeObject <Dictionary <string, object> >(data);
                                foreach (KeyValuePair <string, object> item in attr)
                                {
                                    parms.Add(item.Key.ToLower(), item.Value.ToString());
                                }
                            }
                            else if (data.IndexOf("=") >= 0)
                            {
                                //HTML form POST data
                                dataType = 1;
                            }
                        }
                        else
                        {
                            //get method parameters from query string
                            foreach (var key in context.Request.Query.Keys)
                            {
                                parms.Add(key.ToLower(), context.Request.Query[key].ToString());
                            }
                        }

                        //start building Web API response (find method to execute & return results)
                        var S = new Core(server, context);

                        //load service class from URL path
                        string className  = S.Server.nameSpace + ".Services." + paths[1];
                        string methodName = paths[2];
                        if (paths.Length == 4)
                        {
                            className += "." + paths[2]; methodName = paths[3];
                        }
                        var routes  = new global::Routes(S);
                        var service = routes.FromServiceRoutes(className);
                        if (service == null)
                        {
                            try
                            {
                                Type stype = Type.GetType(className);
                                service = (Service)Activator.CreateInstance(stype, new object[] { S });
                            }
                            catch (Exception) { }
                        }

                        //check if service class was found
                        if (service == null)
                        {
                            S.Response.ContentType = "text/html";
                            context.Response.WriteAsync("no service found");
                            S.Unload();
                            return;
                        }

                        if (dataType == 1)
                        {
                            //parse HTML form POST data and send to new Service instance
                            string[] items = S.Server.UrlDecode(data).Split('&');
                            string[] item;
                            for (var x = 0; x < items.Length; x++)
                            {
                                item = items[x].Split('=');
                                service.Form.Add(item[0], item[1]);
                            }
                        }
                        else if (dataType == 2)
                        {
                            //send multi-part file upload data to new Service instance
                            service.Files = form.Files;
                        }

                        //execute method from new Service instance
                        Type       type   = Type.GetType(className);
                        MethodInfo method = type.GetMethod(methodName);

                        //try to cast params to correct types
                        ParameterInfo[] methodParams = method.GetParameters();

                        paramVals = new object[methodParams.Length];
                        for (var x = 0; x < methodParams.Length; x++)
                        {
                            //find correct key/value pair
                            param = "";
                            foreach (var item in parms)
                            {
                                if (item.Key == methodParams[x].Name.ToLower())
                                {
                                    param = item.Value;
                                    break;
                                }
                            }

                            if (param == "")
                            {
                                //set default value for empty parameter
                                var t = methodParams[x].ParameterType;
                                if (t == typeof(Int32))
                                {
                                    param = "0";
                                }
                            }

                            //cast params to correct (supported) types
                            if (methodParams[x].ParameterType.Name != "String")
                            {
                                var i = 0;
                                if (int.TryParse(param, out i) == true)
                                {
                                    if (methodParams[x].ParameterType.IsEnum == true)
                                    {
                                        //enum
                                        paramVals[x] = Enum.Parse(methodParams[x].ParameterType, param);
                                    }
                                    else
                                    {
                                        //int
                                        paramVals[x] = Convert.ChangeType(i, methodParams[x].ParameterType);
                                    }
                                }
                                else if (methodParams[x].ParameterType.FullName.Contains("DateTime"))
                                {
                                    if (param == "")
                                    {
                                        paramVals[x] = null;
                                    }
                                    else
                                    {
                                        try
                                        {
                                            paramVals[x] = DateTime.Parse(param);
                                        }
                                        catch (Exception) { }
                                    }
                                }
                                else if (methodParams[x].ParameterType.IsArray)
                                {
                                    var arr = param.Replace("[", "").Replace("]", "").Replace("\r", "").Replace("\n", "").Split(",").Select(a => { return(a.Trim()); }).ToList();
                                    if (methodParams[x].ParameterType.FullName == "System.Int32[]")
                                    {
                                        paramVals[x] = arr.Select(a => { return(int.Parse(a)); }).ToArray();
                                    }
                                    else
                                    {
                                        paramVals[x] = Convert.ChangeType(arr, methodParams[x].ParameterType);
                                    }
                                }
                                else if (methodParams[x].ParameterType.Name.IndexOf("Dictionary") == 0)
                                {
                                    paramVals[x] = (Dictionary <string, string>)S.Util.Serializer.ReadObject(param, typeof(Dictionary <string, string>));
                                }
                                else
                                {
                                    paramVals[x] = Convert.ChangeType(param, methodParams[x].ParameterType);
                                }
                            }
                            else
                            {
                                //string
                                paramVals[x] = param;
                            }
                        }

                        object result = null;

                        try
                        {
                            result = method.Invoke(service, paramVals);
                        }
                        catch (Exception ex)
                        {
                            if (server.environment == Server.enumEnvironment.development)
                            {
                                Console.WriteLine(ex.Message + "\n" + ex.StackTrace);
                            }
                            throw ex;
                        }


                        //finally, unload the Datasilk Core:
                        //close SQL connection, save User info, etc (before sending response)
                        S.Unload();
                        context.Response.ContentType = "text/json";
                        if (result != null)
                        {
                            context.Response.WriteAsync((string)result);
                        }
                        else
                        {
                            context.Response.WriteAsync("{\"error\":\"no content returned\"}");
                        }
                    }
                }
            }

            if (requestType == "" && extension == "")
            {
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                //page request (initialize client-side application) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                requestType = "page";
                var S = new Core(server, context);

                //create instance of Page class based on request URL path
                var html     = "";
                var newpaths = path.Split('?', 2)[0].Split('/');
                var routes   = new global::Routes(S);
                var page     = routes.FromPageRoutes(newpaths[0].ToLower());

                if (page == null)
                {
                    //page is not part of any known routes, try getting page class manually
                    Type type = Type.GetType((S.Server.nameSpace + ".Pages." + (newpaths[0] == "" ? "Login" : S.Util.Str.Capitalize(newpaths[0].Replace("-", " ")).Replace(" ", ""))));
                    page = (Page)Activator.CreateInstance(type, new object[] { S });
                }

                if (page != null)
                {
                    //render page
                    if (form != null)
                    {
                        page.Files = form.Files;
                    }
                    try
                    {
                        html = page.Render(newpaths);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message + "\n" + ex.StackTrace);
                        throw ex;
                    }
                }
                else
                {
                    //show 404 error
                    page = new Page(S);
                    html = page.Error404();
                }

                //unload Datasilk Core
                page = null;
                S.Unload();

                //send response back to client
                S.Response.ContentType = "text/html";
                await S.Response.WriteAsync(html);
            }

            if (server.environment == Server.enumEnvironment.development)
            {
                requestEnd          = DateTime.Now;
                tspan               = requestEnd - requestStart;
                server.requestTime += (tspan.Seconds);
                Console.WriteLine("END REQUEST {0} ms, {1} {2}", tspan.Milliseconds, path, requestType);
                Console.WriteLine("");
            }
        }
Exemple #3
0
        public virtual async Task Run(HttpContext context)
        {
            context.Features.Get <IHttpMaxRequestBodySizeFeature>().MaxRequestBodySize = null;
            var      requestStart = DateTime.Now;
            DateTime requestEnd;
            TimeSpan tspan;
            var      path      = CleanPath(context.Request.Path.ToString());
            var      paths     = path.Split('/').ToArray();
            var      isApiCall = false;

            server.requestCount++;
            if (paths[paths.Length - 1].IndexOf(".") > 0)
            {
                //do not process files, but instead return a 404 error
                context.Response.StatusCode = 404;
                return;
            }

            if (server.environment == Server.Environment.development)
            {
                Console.WriteLine("{0} GET {1}", DateTime.Now.ToString("hh:mm:ss"), path);

                //optionally, wipe Scaffold cache to enable developer updates to html files when server is running
                Server.Scaffold = new Dictionary <string, SerializedScaffold>();
            }

            if (paths.Length > 1 && paths[0] == "api")
            {
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                //run a web service via ajax (e.g. /api/namespace/class/function) //////////////////////////////////////////////////////////////////////////////////////////////////////
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                //execute web service
                server.apiRequestCount++;
                isApiCall = true;

                //get parameters from request body, including page id
                var      parms = new Dictionary <string, string>();
                object[] paramVals;
                var      param = "";
                string   data  = "";
                if (context.Request.ContentType != null && context.Request.ContentType.IndexOf("multipart/form-data") < 0 && context.Request.Body.CanRead)
                {
                    //get POST data from request
                    byte[] bytes = new byte[0];
                    using (MemoryStream ms = new MemoryStream())
                    {
                        context.Request.Body.CopyTo(ms);
                        bytes = ms.ToArray();
                    }
                    data = Encoding.UTF8.GetString(bytes, 0, bytes.Length).Trim();
                }

                if (data.Length > 0)
                {
                    if (data.IndexOf("Content-Disposition") < 0 && data.IndexOf("{") >= 0 && data.IndexOf("}") > 0 && data.IndexOf(":") > 0)
                    {
                        //get method parameters from POST S.ajax.post()
                        Dictionary <string, object> attr = JsonConvert.DeserializeObject <Dictionary <string, object> >(data);
                        foreach (KeyValuePair <string, object> item in attr)
                        {
                            parms.Add(item.Key.ToLower(), item.Value.ToString());
                        }
                    }
                }
                else
                {
                    //get method parameters from query string
                    foreach (var key in context.Request.Query.Keys)
                    {
                        parms.Add(key.ToLower(), context.Request.Query[key].ToString());
                    }
                }

                //load service class from URL path
                string className  = server.nameSpace + ".Services." + paths[1].Replace("-", "").Replace(" ", "");
                string methodName = paths[2];
                if (paths.Length == 4)
                {
                    className += "." + paths[2]; methodName = paths[3];
                }
                var service = routes.FromServiceRoutes(context, className);
                if (service == null)
                {
                    try
                    {
                        service = (Service)Activator.CreateInstance(Type.GetType(className), new object[] { context });
                    }
                    catch (Exception) { }
                }

                //check if service class was found
                if (service == null)
                {
                    context.Response.ContentType = "text/html";
                    context.Response.StatusCode  = 500;
                    await context.Response.WriteAsync("no service found");

                    return;
                }

                //execute method from new Service instance
                Type       type   = Type.GetType(className);
                MethodInfo method = type.GetMethod(methodName);

                //try to cast params to correct types
                ParameterInfo[] methodParams = method.GetParameters();

                paramVals = new object[methodParams.Length];
                for (var x = 0; x < methodParams.Length; x++)
                {
                    //find correct key/value pair
                    param = "";
                    foreach (var item in parms)
                    {
                        if (item.Key == methodParams[x].Name.ToLower())
                        {
                            param = item.Value;
                            break;
                        }
                    }

                    if (param == "")
                    {
                        //set default value for empty parameter
                        var t = methodParams[x].ParameterType;
                        if (t == typeof(Int32))
                        {
                            param = "0";
                        }
                    }

                    //cast params to correct (supported) types
                    if (methodParams[x].ParameterType.Name != "String")
                    {
                        if (int.TryParse(param, out int i) == true)
                        {
                            if (methodParams[x].ParameterType.IsEnum == true)
                            {
                                //enum
                                paramVals[x] = Enum.Parse(methodParams[x].ParameterType, param);
                            }
                            else
                            {
                                //int
                                paramVals[x] = Convert.ChangeType(i, methodParams[x].ParameterType);
                            }
                        }
                        else if (methodParams[x].ParameterType.FullName.Contains("DateTime"))
                        {
                            if (param == "")
                            {
                                paramVals[x] = null;
                            }
                            else
                            {
                                try
                                {
                                    paramVals[x] = DateTime.Parse(param);
                                }
                                catch (Exception) { }
                            }
                        }
                        else if (methodParams[x].ParameterType.IsArray)
                        {
                            var arr = param.Replace("[", "").Replace("]", "").Replace("\r", "").Replace("\n", "").Split(",").Select(a => { return(a.Trim()); }).ToList();
                            if (methodParams[x].ParameterType.FullName == "System.Int32[]")
                            {
                                paramVals[x] = arr.Select(a => { return(int.Parse(a)); }).ToArray();
                            }
                            else
                            {
                                paramVals[x] = Convert.ChangeType(arr, methodParams[x].ParameterType);
                            }
                        }
                        else if (methodParams[x].ParameterType.Name.IndexOf("Dictionary") == 0)
                        {
                            paramVals[x] = (Dictionary <string, string>)Serializer.ReadObject(param, typeof(Dictionary <string, string>));
                        }
                        else
                        {
                            paramVals[x] = Convert.ChangeType(param, methodParams[x].ParameterType);
                        }
                    }
                    else
                    {
                        //string
                        paramVals[x] = param;
                    }
                }

                object result = null;

                try
                {
                    result = method.Invoke(service, paramVals);
                }
                catch (Exception ex)
                {
                    if (server.environment == Server.Environment.development)
                    {
                        Console.WriteLine(ex.InnerException.Message + "\n" + ex.InnerException.StackTrace);
                    }
                    throw ex.InnerException;
                }
                service.Unload();

                //finally, unload the Datasilk Core:
                //close SQL connection, save User info, etc (before sending response)
                context.Response.ContentType = "text/json";
                if (result != null)
                {
                    await context.Response.WriteAsync((string)result);
                }
                else
                {
                    await context.Response.WriteAsync("{\"error\":\"no content returned\"}");
                }
            }
            else
            {
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                //page request (initialize client-side application) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                server.pageRequestCount++;

                //create instance of Page class based on request URL path
                var html     = "";
                var newpaths = path.Split('?', 2)[0].Split('/');
                var page     = routes.FromPageRoutes(context, newpaths[0].ToLower());

                if (page == null)
                {
                    //page is not part of any known routes, try getting page class manually
                    Type type = Type.GetType((server.nameSpace + ".Pages." + (newpaths[0] == "" ? "Login" : newpaths[0].Capitalize().Replace("-", " ")).Replace(" ", "")));
                    page = (Page)Activator.CreateInstance(type, new object[] { context });
                }

                if (page != null)
                {
                    //render page
                    try
                    {
                        html = page.Render(newpaths);
                    }
                    catch (Exception ex)
                    {
                        if (server.environment == Server.Environment.development)
                        {
                            Console.WriteLine(ex.Message + "\n" + ex.StackTrace);
                        }
                        throw ex;
                    }
                }
                else
                {
                    //show 404 error
                    page = new Page(context);
                    html = page.Error404();
                }

                //unload Datasilk Core
                page.Unload();
                page = null;

                //send response back to client
                context.Response.ContentType = "text/html";
                await context.Response.WriteAsync(html);
            }

            if (server.environment == Server.Environment.development)
            {
                requestEnd          = DateTime.Now;
                tspan               = requestEnd - requestStart;
                server.requestTime += (tspan.Seconds);
                Console.WriteLine("END REQUEST {0} ms, {1} {2}", tspan.Milliseconds, path, isApiCall ? "Web API" : "Page");
                Console.WriteLine("");
            }
        }