/// <summary> /// Register the given method with the specified url. /// </summary> /// <param name="url">The URL that will be used, relative to the service.api handler.</param> /// <param name="mi">The method to be invoked.</param> public void RegisterHandler(object instance, String method, String url, MethodInfo mi) { RestMethodInfo rmi; // // Create the root level if it does not exist. // if (registeredHandlers == null) { registeredHandlers = new ArrayList(); } // // Create the REST state method information. // rmi = new RestMethodInfo(instance, method.ToUpper(), url, mi); // // Add the new method information into the list of handlers. // registeredHandlers.Add(rmi); }
/// <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()); if (rmi.uriTemplate.ToString() != "/version" && rmi.uriTemplate.ToString() != "/login" && rmi.uriTemplate.ToString() != "/help" && rmi.methodInfo.GetCustomAttributes(typeof(RestApiAnonymous), true).Length == 0) { String PathAndQuery = context.Request.RawUrl.ToLower(); PathAndQuery = PathAndQuery.Substring(context.Request.FilePath.Length + 1); AuthenticationManager.SetupSessionForRequest(context.Request.QueryString["api_session"], false); AuthenticationManager.VerifySignature(context.Request.Url, PathAndQuery, context.Request.QueryString["api_session"]); } } catch (Exception e) { RESTException restEx = e as RESTException; if (restEx != null) { result = new RestErrorMessage(restEx); } else { result = new RestErrorMessage(System.Net.HttpStatusCode.InternalServerError, e.ToString(), string.Empty); } } // // Perform the actual method call. // if (result == null) { 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))); } } catch (Exception e) { 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.InternalServerError, e.ToString(), 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; } }