internal ServiceRequestEventArgs(ServiceRequest request, ServiceResponse response) { this.Request = request; this.Response = response; }
/// <summary> /// Generates a script tag to embed a model including instance data into a server-rendered view. /// Supports initial customization of the model via an initialization delegate. /// </summary> /// <param name="query"></param> /// <param name="init"></param> /// <returns></returns> static void Model(TextWriter writer, object query, Delegate init) { // Raise the begin model event if (BeginModel != null) { BeginModel(query, EventArgs.Empty); } // Get the roots for each query var roots = new List <KeyValuePair <string, ServiceRequest.Query> >(); foreach (var prop in query.GetType().GetProperties()) { // Get the value of the model property var m = prop.GetValue(query, null); if (m == null) { continue; } // Convert the value into a query, if necessary var q = m as ServiceRequest.Query ?? Query(m); if (q != null) { q.LoadRoots(null); roots.Add(new KeyValuePair <string, ServiceRequest.Query>(prop.Name, q)); } } // Create the request object ServiceRequest request = new ServiceRequest(roots.Select(r => r.Value).ToArray()); // Synthesize init new changes for new query roots ModelTransaction initChanges = (ModelTransaction)request.Queries .SelectMany(q => q.Roots) .Where(i => i.IsNew) .Select(i => new ModelInitEvent.InitNew(i)) .Cast <ModelEvent>() .ToList(); // Perform the initialization action, if specified if (init != null) { // Determine matching parameters var parameters = new object[init.Method.GetParameters().Length]; foreach (var p in init.Method.GetParameters()) { foreach (var root in roots) { if (p.Name.Equals(root.Key, StringComparison.InvariantCultureIgnoreCase)) { // List parameter if (p.ParameterType.IsArray) { parameters[p.Position] = root.Value.Roots.Select(r => r.Instance).ToArray(); } // Instance parameter else { parameters[p.Position] = root.Value.Roots.Select(r => r.Instance).FirstOrDefault(); } break; } } // Throw an exception if the model property value cannot be cast to the corresponding initialization parameter if (parameters[p.Position] != null && !p.ParameterType.IsAssignableFrom(parameters[p.Position].GetType())) { throw new ArgumentException(String.Format("The model property '{0}' cannot be converted into the initialization parameter type of '{1}'.", p.Name, p.ParameterType.FullName)); } // Throw an exception if a valid model parameter could not be found to pass to the initialization delegate if (parameters[p.Position] == null && query.GetType().GetProperty(p.Name, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance) == null) { throw new ArgumentException(String.Format("A model property could not be found to pass to the initialization parameter '{0}'.", p.Name)); } } // Perform initialization while capturing changes initChanges.Record(() => { // Prevent property change rules from running until after initialization ModelEventScope.Perform(() => init.DynamicInvoke(parameters)); }); } // Invoke the request queries ServiceResponse response = request.Invoke(initChanges); response.Model = roots.ToDictionary(r => r.Key, r => r.Value); foreach (var q in response.Model.Values) { q.ReducePaths(); } // Track the current model roots to support server template rendering foreach (var root in response.Model) { Templates.Page.Current.Model[root.Key] = root.Value.IsList ? (object)root.Value.Roots : (root.Value.Roots.Length == 1 ?root.Value.Roots[0] : null); } // Write the model embed script to the writer writer.Write("<script type=\"text/javascript\">$exoweb("); JsonUtility.Serialize(writer, response); writer.Write(");</script>"); // Raise the end model event if (EndModel != null) { EndModel(query, EventArgs.Empty); } }
/// <summary> /// Processes incoming requests and routes them to the appropriate JSON handler method. /// </summary> /// <param name="context"></param> void IHttpHandler.ProcessRequest(HttpContext context) { IsExecuting = true; try { // Perform the requested operation switch (context.Request.PathInfo) { case "/GetType": // Enable response caching context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.SetExpires(DateTime.Now.AddDays(7)); context.Response.Cache.SetMaxAge(TimeSpan.FromDays(7)); // Output the type metadata context.Response.ContentType = "application/json"; JsonUtility.Serialize(context.Response.OutputStream, new ServiceRequest(context.Request.QueryString["type"].Replace("\"", "")).Invoke(null)); break; case "/LogError": // Raise the error event context.Response.ContentType = "application/json"; ExoWeb.OnError(JsonUtility.Deserialize <ServiceError>(context.Request.InputStream)); break; default: // Deserialize the request context.Response.Cache.SetCacheability(HttpCacheability.NoCache); context.Response.Cache.SetNoStore(); ServiceRequest request = JsonUtility.Deserialize <ServiceRequest>(context.Request.InputStream); // Invoke the request and output the response context.Response.ContentType = "application/json"; using (var test = new StringWriter()) { JsonUtility.Serialize(test, request.Invoke(null)); context.Response.Write(test.ToString()); } break; } } catch (Exception e) { // look for an ExoModel.ModelException that may display a more // abstracted or informative message in the UI. Otherwise, use // the inner most exception Exception reported = e; for (Exception x = e; x != null; x = x.InnerException) { reported = x; if (x is ModelException) { break; } } // Create an error to log var error = new ServiceError(); error.Type = reported.GetType().FullName; error.StackTrace = GetFullStackTrace(e); error.Message = reported.Message; error.Url = context.Request.RawUrl; if (error.AdditionalInfo == null) { error.AdditionalInfo = new Dictionary <string, object>(); } //error.AdditionalInfo.Add("Client.RequestJson", json); // Raise the error event ExoWeb.OnError(error); // Also send the error information to the client context.Response.Clear(); context.Response.ContentType = "application/json"; context.Response.StatusCode = 500; // internal server error // Enable error information on client if (ExoWeb.EnableExceptionInformation) { context.Response.AddHeader("jsonerror", "true"); // Ensure IIS 7 doesn't intercept the error context.Response.TrySkipIisCustomErrors = true; } context.Response.Write(ExoWeb.ToJson(error)); } }