/// <summary> /// Process the web request. /// </summary> /// <param name="context">The context of this single web request.</param> public void ProcessRequest(HttpContext context) { UriTemplateMatch templateMatch = null; RestMethodInfo rmi = null; ArrayList finalParameters = null; Object result = null, p; // // Initialization phase, register all handlers and then find a match. // try { // // Register all handlers. // RegisterHandlers(); String baseUrl = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + context.Request.FilePath; rmi = FindHandler(context.Request.HttpMethod.ToUpper(), new Uri(baseUrl), context.Request.Url, ref templateMatch); if (rmi == null) { throw new MissingMethodException(); } } catch (Exception e) { context.Response.Write(String.Format("Exception occurred at init: {0}", e.Message + e.StackTrace)); return; } // // Parse out any parameters for the method call. // try { finalParameters = new ArrayList(); // // Walk each parameter in the method and see if we can convert // one of the query variables to the proper type. // foreach (ParameterInfo pi in rmi.methodInfo.GetParameters()) { try { p = null; if (typeof(Stream).IsAssignableFrom(pi.ParameterType)) { p = context.Request.InputStream; } else if (templateMatch.BoundVariables.AllKeys.Contains(pi.Name.ToUpper()) == true) { p = templateMatch.BoundVariables[pi.Name.ToUpper()]; if (p != null) { if (typeof(List <String>).IsAssignableFrom(pi.ParameterType)) { p = p.ToString().Split(new char[1] { ',' }).ToList <String>(); } else { p = Convert.ChangeType(p, pi.ParameterType); } } } } catch { p = null; } finalParameters.Add(p); } } catch (Exception e) { context.Response.Write(String.Format("Exception occurred at parameter parse: {0} at {1}", e.Message, e.StackTrace)); return; } // // Force the context to be anonymous, then authenticate if the user // is calling a non-anonymous method. // try { ArenaContext.Current.SetWebServiceProperties(ArenaContext.Current.CreatePrincipal(""), new Arena.Core.Person()); String PathAndQuery = String.Empty; if (rmi.uriTemplate.ToString() != "/version" && rmi.uriTemplate.ToString() != "/login" && rmi.uriTemplate.ToString() != "/help" && rmi.methodInfo.GetCustomAttributes(typeof(RestApiAnonymous), true).Length == 0 && rmi.methodInfo.GetCustomAttributes(typeof(RestApiCustom), true).Length == 0) { PathAndQuery = context.Request.Path + "?" + context.Server.UrlDecode(context.Request.QueryString.ToString()); PathAndQuery = PathAndQuery.Substring(context.Request.FilePath.Length + 1).ToLower(); PathAndQuery = PathAndQuery.Substring(0, PathAndQuery.IndexOf("api_sig") - 1); string apiSession = context.Request.Params["api_session"]; // Loop through non-production environments to determine where we are bool checkForExpiration = true; if (ConfigurationSettings.AppSettings["EnableAutomaticSessionExtension"] == "true") { foreach (String url in ConfigurationSettings.AppSettings["Environments"].Split(',')) { if (HttpContext.Current.Request.Url.ToString().IndexOf(url) > -1) { checkForExpiration = false; } } } if (!checkForExpiration) { CoreApiSessionService.ExtendExpiration(apiSession); } AuthenticationManager.SetupSessionForRequest(apiSession, false); AuthenticationManager.VerifySignature(context.Request.Url, PathAndQuery, apiSession); } else if (rmi.methodInfo.GetCustomAttributes(typeof(RestApiCustom), true).Length != 0) { PathAndQuery = context.Request.Path; PathAndQuery = PathAndQuery.Substring(context.Request.FilePath.Length + 1).ToLower(); string secret = String.Empty; foreach (String element in context.Request.Form.AllKeys) { if (element != "api_sig" && element.IndexOf("btn_") == -1) { PathAndQuery += (PathAndQuery.IndexOf("?") > 0 ? "&" : "?"); PathAndQuery += element.Trim() + "=" + context.Request.Form[element]; } if (element == "api_session") { CoreApiSessionService cass = new CoreApiSessionService(); secret = cass.GetSecretBySessionId(context.Request.Form[element]); } } if (!String.IsNullOrEmpty(secret)) { string sig = EncryptString(secret + "_" + PathAndQuery.ToLower()); if (sig.ToLower() != context.Request.Params["api_sig"].ToLower()) { throw new Exception("Invalid signature. " + "Path: " + context.Request.Path + "?" + context.Server.UrlDecode(context.Request.QueryString.ToString()) + "; Signatured string: " + secret + "_" + PathAndQuery.ToLower()); } } else { throw new Exception("Invalid session."); } } } catch (Exception e) { RESTException restEx = e as RESTException; if (restEx != null) { // result = new RestErrorMessage(System.Net.HttpStatusCode.Conflict, e.Message + " " + e.StackTrace, string.Empty); result = new RestErrorMessage(restEx); } else { result = new RestErrorMessage(System.Net.HttpStatusCode.OK, e.Message, string.Empty); } } // // Perform the actual method call. // if (result == null) { // NPM: Capture session and user id's for logging string sessionid = System.Web.HttpContext.Current.Request.Params["sessionid"]; int? personid = PersonId(); try { // // Set some default response information. // context.Response.ContentType = "application/xml; charset=utf-8"; if (TypeIsServiceContract(rmi.instance.GetType()) == true) { // // Run the request inside of a operation context so response information // can be set. This is a bit of a cheat, but it works. // WebChannelFactory <NoOp> factory = new WebChannelFactory <NoOp>(new Uri("http://localhost/")); NoOp channel = factory.CreateChannel(); using (new OperationContextScope((IContextChannel)channel)) { result = rmi.methodInfo.Invoke(rmi.instance, (object[])finalParameters.ToArray(typeof(object))); if (WebOperationContext.Current.OutgoingResponse.ContentType != null) { context.Response.ContentType = WebOperationContext.Current.OutgoingResponse.ContentType; } } } else { // // This is a standard method call, just call it. // result = rmi.methodInfo.Invoke(rmi.instance, (object[])finalParameters.ToArray(typeof(object))); } // NPM: Log the call as a SUCCESS ApiSessionLogService.Log(sessionid, personid, context.Request.PathInfo.ToLower(), "SUCCESS", ""); } catch (Exception e) { // NPM: Log the call as a FAILURE ApiSessionLogService.Log(sessionid, personid, context.Request.PathInfo.ToLower(), "FAILURE", e.InnerException.Message); RESTException restEx; if (e.InnerException != null) { e = e.InnerException; } restEx = e as RESTException; if (restEx != null) { result = new RestErrorMessage(restEx); } else { result = new RestErrorMessage(System.Net.HttpStatusCode.OK, e.Message, string.Empty); } } } // // Deal with the response that was generated. // try { if (result != null) { // // There is probably a better way to do this, but this is the best // I can come up with. Somebody feel free to make this cleaner. // if (typeof(Stream).IsAssignableFrom(result.GetType()) == true) { Stream s = (Stream)result; int count; // // Response is a data stream, just copy it to the response // stream. // do { byte[] buf = new byte[8192]; count = s.Read(buf, 0, 8192); context.Response.BinaryWrite(buf); } while (count > 0); } else if (typeof(Message).IsAssignableFrom(result.GetType()) == true) { Message msg = (Message)result; StringBuilder sb = new StringBuilder(); StringWriter sw = new StringWriter(sb); XmlTextWriter xtw = new XmlTextWriter(sw); // // Response is a Message object. Write it out as an XML // stream. // msg.WriteMessage(xtw); context.Response.Write(sb.ToString()); } else { DataContractSerializer serializer = new DataContractSerializer(result.GetType()); // // Otherwise, use the DataContractSerializer to convert the object into // an XML stream. // serializer.WriteObject(context.Response.OutputStream, result); } } } catch (Exception e) { context.Response.Write(String.Format("Exception sending response: {0}", e.Message)); return; } }
/// <summary> /// Process the web request. /// </summary> /// <param name="context">The context of this single web request.</param> public void ProcessRequest(HttpContext context) { UriTemplateMatch templateMatch = null; RestMethodInfo rmi = null; ArrayList finalParameters = null; Object result = null, p; // // Initialization phase, register all handlers and then find a match. // try { // // Register all handlers. // RegisterHandlers(); String baseUrl = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + context.Request.FilePath; rmi = FindHandler(context.Request.HttpMethod.ToUpper(), new Uri(baseUrl), context.Request.Url, ref templateMatch); if (rmi == null) throw new MissingMethodException(); } catch (Exception e) { context.Response.Write(String.Format("Exception occurred at init: {0}", e.Message + e.StackTrace)); return; } // // Parse out any parameters for the method call. // try { finalParameters = new ArrayList(); // // Walk each parameter in the method and see if we can convert // one of the query variables to the proper type. // foreach (ParameterInfo pi in rmi.methodInfo.GetParameters()) { try { p = null; if (typeof(Stream).IsAssignableFrom(pi.ParameterType)) { p = context.Request.InputStream; } else if (templateMatch.BoundVariables.AllKeys.Contains(pi.Name.ToUpper()) == true) { p = templateMatch.BoundVariables[pi.Name.ToUpper()]; if (p != null) { if (typeof(List<String>).IsAssignableFrom(pi.ParameterType)) { p = p.ToString().Split(new char[1] { ',' }).ToList<String>(); } else p = Convert.ChangeType(p, pi.ParameterType); } } } catch { p = null; } finalParameters.Add(p); } } catch (Exception e) { context.Response.Write(String.Format("Exception occurred at parameter parse: {0} at {1}", e.Message, e.StackTrace)); return; } // // Force the context to be anonymous, then authenticate if the user // is calling a non-anonymous method. // try { ArenaContext.Current.SetWebServiceProperties(ArenaContext.Current.CreatePrincipal(""), new Arena.Core.Person()); String PathAndQuery = String.Empty; if (rmi.uriTemplate.ToString() != "/version" && rmi.uriTemplate.ToString() != "/login" && rmi.uriTemplate.ToString() != "/help" && rmi.methodInfo.GetCustomAttributes(typeof(RestApiAnonymous), true).Length == 0 && rmi.methodInfo.GetCustomAttributes(typeof(RestApiCustom), true).Length == 0) { PathAndQuery = context.Request.Path + "?" + context.Server.UrlDecode(context.Request.QueryString.ToString()); PathAndQuery = PathAndQuery.Substring(context.Request.FilePath.Length + 1).ToLower(); PathAndQuery = PathAndQuery.Substring(0, PathAndQuery.IndexOf("api_sig") - 1); string apiSession = context.Request.Params["api_session"]; // Loop through non-production environments to determine where we are bool checkForExpiration = true; if (ConfigurationSettings.AppSettings["EnableAutomaticSessionExtension"] == "true") { foreach (String url in ConfigurationSettings.AppSettings["Environments"].Split(',')) { if (HttpContext.Current.Request.Url.ToString().IndexOf(url) > -1) { checkForExpiration = false; } } } if (!checkForExpiration) { CoreApiSessionService.ExtendExpiration(apiSession); } AuthenticationManager.SetupSessionForRequest(apiSession, false); AuthenticationManager.VerifySignature(context.Request.Url, PathAndQuery, apiSession); } else if (rmi.methodInfo.GetCustomAttributes(typeof(RestApiCustom), true).Length != 0) { PathAndQuery = context.Request.Path; PathAndQuery = PathAndQuery.Substring(context.Request.FilePath.Length + 1).ToLower(); string secret = String.Empty; foreach (String element in context.Request.Form.AllKeys) { if (element != "api_sig" && element.IndexOf("btn_") == -1) { PathAndQuery += (PathAndQuery.IndexOf("?") > 0 ? "&" : "?"); PathAndQuery += element.Trim() + "=" + context.Request.Form[element]; } if (element == "api_session") { CoreApiSessionService cass = new CoreApiSessionService(); secret = cass.GetSecretBySessionId(context.Request.Form[element]); } } if (!String.IsNullOrEmpty(secret)) { string sig = EncryptString(secret + "_" + PathAndQuery.ToLower()); if (sig.ToLower() != context.Request.Params["api_sig"].ToLower()) { throw new Exception("Invalid signature. " + "Path: " + context.Request.Path + "?" + context.Server.UrlDecode(context.Request.QueryString.ToString()) + "; Signatured string: " + secret + "_" + PathAndQuery.ToLower()); } } else { throw new Exception("Invalid session."); } } } catch (Exception e) { RESTException restEx = e as RESTException; if (restEx != null) { // result = new RestErrorMessage(System.Net.HttpStatusCode.Conflict, e.Message + " " + e.StackTrace, string.Empty); result = new RestErrorMessage(restEx); } else { result = new RestErrorMessage(System.Net.HttpStatusCode.OK, e.Message, string.Empty); } } // // Perform the actual method call. // if (result == null) { // NPM: Capture session and user id's for logging string sessionid = System.Web.HttpContext.Current.Request.Params["sessionid"]; int? personid = PersonId(); try { // // Set some default response information. // context.Response.ContentType = "application/xml; charset=utf-8"; if (TypeIsServiceContract(rmi.instance.GetType()) == true) { // // Run the request inside of a operation context so response information // can be set. This is a bit of a cheat, but it works. // WebChannelFactory<NoOp> factory = new WebChannelFactory<NoOp>(new Uri("http://localhost/")); NoOp channel = factory.CreateChannel(); using (new OperationContextScope((IContextChannel)channel)) { result = rmi.methodInfo.Invoke(rmi.instance, (object[])finalParameters.ToArray(typeof(object))); if (WebOperationContext.Current.OutgoingResponse.ContentType != null) context.Response.ContentType = WebOperationContext.Current.OutgoingResponse.ContentType; } } else { // // This is a standard method call, just call it. // result = rmi.methodInfo.Invoke(rmi.instance, (object[])finalParameters.ToArray(typeof(object))); } // NPM: Log the call as a SUCCESS ApiSessionLogService.Log(sessionid, personid, context.Request.PathInfo.ToLower(), "SUCCESS", ""); } catch (Exception e) { // NPM: Log the call as a FAILURE ApiSessionLogService.Log(sessionid, personid, context.Request.PathInfo.ToLower(), "FAILURE", e.InnerException.Message); RESTException restEx; if (e.InnerException != null) e = e.InnerException; restEx = e as RESTException; if (restEx != null) { result = new RestErrorMessage(restEx); } else { result = new RestErrorMessage(System.Net.HttpStatusCode.OK, e.Message, string.Empty); } } } // // Deal with the response that was generated. // try { if (result != null) { // // There is probably a better way to do this, but this is the best // I can come up with. Somebody feel free to make this cleaner. // if (typeof(Stream).IsAssignableFrom(result.GetType()) == true) { Stream s = (Stream)result; int count; // // Response is a data stream, just copy it to the response // stream. // do { byte[] buf = new byte[8192]; count = s.Read(buf, 0, 8192); context.Response.BinaryWrite(buf); } while (count > 0); } else if (typeof(Message).IsAssignableFrom(result.GetType()) == true) { Message msg = (Message)result; StringBuilder sb = new StringBuilder(); StringWriter sw = new StringWriter(sb); XmlTextWriter xtw = new XmlTextWriter(sw); // // Response is a Message object. Write it out as an XML // stream. // msg.WriteMessage(xtw); context.Response.Write(sb.ToString()); } else { DataContractSerializer serializer = new DataContractSerializer(result.GetType()); // // Otherwise, use the DataContractSerializer to convert the object into // an XML stream. // serializer.WriteObject(context.Response.OutputStream, result); } } } catch (Exception e) { context.Response.Write(String.Format("Exception sending response: {0}", e.Message)); return; } }