/// <summary> /// This method is called in response to any request where the URL ends with a .ajax /// extension, which Sprocket uses to designate an Ajax request from an XmlHttpRequest /// call in the browser. The posted data contains information that this method uses to /// find the correct ISprocketModule implementation. It then uses reflection to get /// the requested method from the module, converts the javascript arguments into native /// CLR types and passes them to the method, after which it takes the returned data and /// writes it to the output stream ready for the XmlHttpRequest object to complete its /// call. Note that data transport uses JSON encoding. See the JSON class in the /// Sprocket.System namespace for information. /// </summary> /// <param name="context">The current HttpContext object.</param> internal void ProcessRequest(HttpContext context) { isAjaxRequest = true; System.Diagnostics.Debug.WriteLine("Start of AJAX page request..."); Dictionary <string, object> responseData = new Dictionary <string, object>(); try { // load the post data from the http stream byte[] posted = new byte[context.Request.InputStream.Length]; context.Request.InputStream.Read(posted, 0, posted.Length); // interpret the stream as a JSON string and parse it into a dictionary string strData = System.Text.Encoding.ASCII.GetString(posted); IDictionary <string, object> data = (IDictionary <string, object>)JSON.Parse(strData); // extract the base page time stamp //pageTimeStamp = new DateTime(long.Parse(data["LoadTimeStamp"].ToString())); //System.Diagnostics.Debug.WriteLine("Extracted page time stamp of " + pageTimeStamp.Ticks.ToString()); //if (OnAjaxRequestTimeStampCheck != null) //{ // Result result = new Result(); // OnAjaxRequestTimeStampCheck(pageTimeStamp, result); // if (!result.Succeeded) // throw new AjaxSessionExpiredException(result.Message); //} // extract the module and method name string fullname; try { fullname = data["ModuleName"].ToString(); } catch (Exception ex) { throw; } int n = fullname.LastIndexOf("."); if (n == -1) { throw new AjaxException("Method name specified incorrectly. Expected format ModuleNamespace.MethodName.\nThe following incorrect format was supplied: " + data["ModuleName"]); } string moduleNamespace = fullname.Substring(0, n); string methodName = fullname.Substring(n + 1, fullname.Length - (n + 1)); // extract the authentication key if (data["AuthKey"].ToString() != WebAuthentication.AuthKeyPlaceholder) { authKey = new Guid(data["AuthKey"].ToString()); } // extract the source URL SprocketPath.Parse(data["SourceURL"].ToString()); // extract the arguments List <object> parsedArguments = (List <object>)data["MethodArgs"]; // find and verify the module/method that should handle this request ISprocketModule module = Core.Instance[moduleNamespace].Module; if (module == null) { throw new AjaxException("The specified module \"" + moduleNamespace + "\" was not found."); } if (Attribute.GetCustomAttribute(module.GetType(), typeof(AjaxMethodHandlerAttribute), false) == null) { throw new SprocketException("The specified module is not marked with AjaxMethodHandlerAttribute. (" + data["ModuleName"] + ")"); } MethodInfo info = module.GetType().GetMethod(methodName); if (info == null) { throw new AjaxException("Failed to find an instance of the specified method. (" + data["ModuleName"] + ")"); } Attribute ajaxMethodAttr = Attribute.GetCustomAttribute(info, typeof(AjaxMethodAttribute)); if (ajaxMethodAttr == null) { throw new AjaxException("Specified method is not marked with AjaxMethodAttribute. (" + data["ModuleName"] + ")"); } AjaxMethodAttribute attr = (AjaxMethodAttribute)ajaxMethodAttr; if (attr.RequiresAuthentication) { if (!WebAuthentication.IsLoggedIn) { AjaxRequestHandler.AbortAjaxCall("You're not currently logged in. Please refresh the page."); } if (AjaxAuthenticate != null) { Result result = AjaxAuthenticate(info); if (!result.Succeeded) { throw new AjaxException(result.Message); } } } // get all of the parameters that the method requires ParameterInfo[] methodParamInfos = info.GetParameters(); // funcinfo is a string representation of the method format and is used for displaying meaningful errors string funcinfo = data["ModuleName"] + "("; if (methodParamInfos.Length > 0) { funcinfo += methodParamInfos[0].ParameterType.Name + " " + methodParamInfos[0].Name; } for (int j = 1; j < methodParamInfos.Length; j++) { funcinfo += ", " + methodParamInfos[j].ParameterType.Name + " " + methodParamInfos[j].Name; } funcinfo += ")"; if (methodParamInfos.Length != parsedArguments.Count) { throw new AjaxException("Method expects " + methodParamInfos.Length + " argument(s) but instead received " + (parsedArguments.Count) + ". Expected format is:\n" + funcinfo); } // create the parameter array and convert each supplied value to its native type object[] prmValuesForMethod = new object[methodParamInfos.Length]; for (int i = 0; i < prmValuesForMethod.Length; i++) { Type t = methodParamInfos[i].ParameterType; try { if (parsedArguments[i] == null) { prmValuesForMethod[i] = null; } else if (t.Name == "Object") { prmValuesForMethod[i] = parsedArguments[i]; } else if (t.Name == "DateTime") { prmValuesForMethod[i] = DateTime.Parse(parsedArguments[i].ToString()); } else if (t.IsArray || t.IsSubclassOf(typeof(IList))) { int elementCount = ((List <object>)parsedArguments[i]).Count; Type arrType = t.GetElementType().MakeArrayType(elementCount); object arr = Array.CreateInstance(t.GetElementType(), ((List <object>)parsedArguments[i]).Count); for (int k = 0; k < ((IList <object>)parsedArguments[i]).Count; k++) { ((IList)arr)[k] = ((IList <object>)parsedArguments[i])[k]; } prmValuesForMethod[i] = arr; } else if (t.GetInterface("IJSONReader") != null) { object obj = Activator.CreateInstance(t); ((IJSONReader)obj).LoadJSON(parsedArguments[i]); prmValuesForMethod[i] = obj; } else if (t.IsAssignableFrom(typeof(Guid)) || t.IsAssignableFrom(typeof(Guid?))) { prmValuesForMethod[i] = parsedArguments[i] == null ? (Guid?)null : new Guid(parsedArguments[i].ToString()); } else if (t.IsAssignableFrom(typeof(long)) || t.IsAssignableFrom(typeof(long?))) { prmValuesForMethod[i] = parsedArguments[i] == null ? (long?)null : long.Parse(parsedArguments[i].ToString()); } else { prmValuesForMethod[i] = Convert.ChangeType(parsedArguments[i], t); } } catch (Exception ex) { string err = "Error converting parameter {0} to type {1}. Expected format is:\n{2}\nReceived Value:\n{3}\nException message:\n{4}"; throw new AjaxException(string.Format(err, i, methodParamInfos[i].ParameterType.Name, funcinfo, parsedArguments[i], ex)); } } // invoke the method if (info.ReturnType == typeof(void)) { info.Invoke(module, prmValuesForMethod); } else { object returnVal = info.Invoke(module, prmValuesForMethod); responseData["Data"] = returnVal; } //context.Response.Write(JSON.Encode(responseData)); } catch (Exception e) { if (!(e is AjaxUserMessageException) && SprocketSettings.GetBooleanValue("CatchExceptions")) { if (e.InnerException != null) { throw e.InnerException; } throw e; } else if (e.InnerException != null) { e = e.InnerException; } responseData["__error"] = e; responseData["__exceptionType"] = e.GetType().FullName; } if (!responseData.ContainsKey("Data")) { responseData["Data"] = null; } //responseData["__timeStamp"] = pageTimeStamp.Ticks; context.Response.Write(JSON.Encode(responseData)); }
/// <summary> /// This method is called in response to any request where the URL ends with a .ajax /// extension, which Sprocket uses to designate an Ajax request from an XmlHttpRequest /// call in the browser. The posted data contains information that this method uses to /// find the correct ISprocketModule implementation. It then uses reflection to get /// the requested method from the module, converts the javascript arguments into native /// CLR types and passes them to the method, after which it takes the returned data and /// writes it to the output stream ready for the XmlHttpRequest object to complete its /// call. Note that data transport uses JSON encoding. See the JSON class in the /// Sprocket.System namespace for information. /// </summary> /// <param name="context">The current HttpContext object.</param> internal void ProcessRequest(HttpContext context) { try { // load the post data from the http stream byte[] posted = new byte[context.Request.InputStream.Length]; context.Request.InputStream.Read(posted, 0, posted.Length); // interpret the stream as a JSON string and parse it into a dictionary IDictionary <string, object> data = (IDictionary <string, object>)JSON.Parse(System.Text.Encoding.ASCII.GetString(posted)); // extract the module and method name string[] func = data["ModuleName"].ToString().Split('.'); if (func.Length != 2) { throw new AjaxException("Method name specified incorrectly. Expected format ModuleName.MethodName.\nThe following incorrect format was supplied: " + data["ModuleName"]); } string moduleName = func[0]; string methodName = func[1]; // extract the authentication key if (data["AuthKey"].ToString() != WebAuthentication.AuthKeyPlaceholder) { AjaxRequestHandler.AuthKey = new Guid(data["AuthKey"].ToString()); } // extract the arguments List <object> parsedArguments = (List <object>)data["MethodArgs"]; // find and verify the module/method that should handle this request ISprocketModule module = SystemCore.Instance[moduleName]; if (module == null) { throw new AjaxException("The specified module \"" + func[0] + "\" was not found."); } if (Attribute.GetCustomAttribute(module.GetType(), typeof(AjaxMethodHandlerAttribute), false) == null) { throw new SprocketException("The specified module is not marked with AjaxMethodHandlerAttribute. (" + data["ModuleName"] + ")"); } MethodInfo info = module.GetType().GetMethod(methodName); if (info == null) { throw new AjaxException("Failed to find an instance of the specified method. (" + data["ModuleName"] + ")"); } Attribute ajaxMethodAttr = Attribute.GetCustomAttribute(info, typeof(AjaxMethodAttribute)); if (ajaxMethodAttr == null) { throw new AjaxException("Specified method is not marked with AjaxMethodAttribute. (" + data["ModuleName"] + ")"); } AjaxMethodAttribute attr = (AjaxMethodAttribute)ajaxMethodAttr; if (attr.RequiresAuthentication) { WebAuthentication auth = (WebAuthentication)SystemCore.Instance["WebAuthentication"]; if (!auth.IsLoggedIn) { throw new AjaxException("You're not currently logged in. Please refresh the page."); } if (OnAjaxRequestAuthenticationCheck != null) { Result result = new Result(); OnAjaxRequestAuthenticationCheck(info, result); if (!result.Succeeded) { throw new AjaxException(result.Message); } } } // get all of the parameters that the method requires ParameterInfo[] methodParamInfos = info.GetParameters(); // funcinfo is a string representation of the method format and is used for displaying meaningful errors string funcinfo = data["ModuleName"] + "("; if (methodParamInfos.Length > 0) { funcinfo += methodParamInfos[0].ParameterType.Name + " " + methodParamInfos[0].Name; } for (int j = 1; j < methodParamInfos.Length; j++) { funcinfo += ", " + methodParamInfos[j].ParameterType.Name + " " + methodParamInfos[j].Name; } funcinfo += ")"; if (methodParamInfos.Length != parsedArguments.Count) { throw new AjaxException("Method expects " + methodParamInfos.Length + " argument(s) but instead received " + (parsedArguments.Count) + ". Expected format is:\n" + funcinfo); } // create the parameter array and convert each supplied value to its native type object[] prmValuesForMethod = new object[methodParamInfos.Length]; for (int i = 0; i < prmValuesForMethod.Length; i++) { Type t = methodParamInfos[i].ParameterType; try { if (parsedArguments[i] == null) { prmValuesForMethod[i] = null; } else if (t.Name == "DateTime") { prmValuesForMethod[i] = DateTime.Parse(parsedArguments[i].ToString()); } else if (t.IsArray || t.IsSubclassOf(typeof(IList))) { int elementCount = ((List <object>)parsedArguments[i]).Count; Type arrType = t.GetElementType().MakeArrayType(elementCount); object arr = Array.CreateInstance(t.GetElementType(), ((List <object>)parsedArguments[i]).Count); for (int k = 0; k < ((IList <object>)parsedArguments[i]).Count; k++) { ((IList)arr)[k] = ((IList <object>)parsedArguments[i])[k]; } prmValuesForMethod[i] = arr; } else if (t.GetInterface("IJSONReader") != null) { object obj = Activator.CreateInstance(t); ((IJSONReader)obj).LoadJSON(parsedArguments[i]); prmValuesForMethod[i] = obj; } else if (t.IsAssignableFrom(typeof(Guid)) || t.IsAssignableFrom(typeof(Guid?))) { prmValuesForMethod[i] = parsedArguments[i] == null ? (Guid?)null : new Guid(parsedArguments[i].ToString()); } else if (t.Name.StartsWith("Nullable`")) { prmValuesForMethod[i] = Convert.ChangeType(parsedArguments[i], t.GetGenericArguments()[0]); } else { prmValuesForMethod[i] = Convert.ChangeType(parsedArguments[i], t); } } catch (Exception ex) { string err = "Error converting parameter {0} to type {1}. Expected format is:\n{2}\nReceived Value:\n{3}\nException message:\n{4}"; throw new AjaxException(string.Format(err, i, methodParamInfos[i].ParameterType.Name, funcinfo, parsedArguments[i], ex)); } } // invoke the method if (info.ReturnType == typeof(void)) { info.Invoke(module, prmValuesForMethod); context.Response.Write("{}"); } else { object returnVal = info.Invoke(module, prmValuesForMethod); context.Response.Write(JSON.Encode(returnVal)); } } catch (Exception e) { StringWriter writer = new StringWriter(); JSON.EncodeCustomObject(writer, new KeyValuePair <string, object>("__error", e)); context.Response.Write(writer.ToString()); } }