using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Web.Mvc; using System.Xml.Serialization; using Website.UI; namespace Website.Controllers { /// /// Sample Action Filter showing how to modify the ActionResult in a helpful way. /// This particular filter will normally not do anything at all, /// but if you attach the "renderMode" querystring to a request /// it will replace the current ActionResult with the appropriate /// serialized data. /// public class SerializableViewDataAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { // Get the (lowercase) render mode from the querystring var method = filterContext.HttpContext.Request .QueryString["renderMode"] ?? string.Empty; method = method.ToLowerInvariant(); // If this isn't a serializable request, just return if(string.IsNullOrEmpty(method)) return; var viewData = new AsynchronousViewDataResponse(filterContext); // Decide which method to use switch (method) { case("xml"): filterContext.Result = getXmlResult(viewData); break; case("json"): filterContext.Result = getJsonResult(viewData); break; } } private static ActionResult getJsonResult(AsynchronousViewDataResponse viewData) { return new JsonResult { Data = viewData }; } private static ActionResult getXmlResult(AsynchronousViewDataResponse viewData) { // There's no "XmlResult" so we have to serialize it ourselves var sb = new StringBuilder(); using (var writer = new StringWriter(sb)) { sb.AppendLine(""); sb.AppendFormat("\n", viewData.IsError); // TODO: Make output XML-friendly if (!string.IsNullOrEmpty(viewData.Message)) sb.AppendFormat("{0}\n", viewData.Message); if (viewData.Model != null) { sb.Append(""); new XmlSerializer(viewData.Model.GetType()).Serialize(writer, viewData.Model); sb.AppendLine(""); } if (viewData.Values != null && viewData.Values.Count > 0) { sb.AppendLine(""); foreach (var val in viewData.Values) sb.AppendFormat("{1}\n", val.Key, val.Value); sb.AppendLine(""); } sb.Append(""); } // ...then pass it into a ContentResult return new ContentResult { ContentType = "text/xml", Content = sb.ToString() }; } protected class AsynchronousViewDataResponse : AsynchronousResponse { public const string ERROR_MESSAGE_KEY = "ErrorMessage"; #region Constructors public AsynchronousViewDataResponse() { PopulateResponseData(new Dictionary(), null, null); } public AsynchronousViewDataResponse(ViewDataDictionary viewData) { PopulateResponseData(viewData, viewData.Model, null); } public AsynchronousViewDataResponse(ActionExecutedContext context) { var controller = ((Controller)context.Controller); // Only show the exception if it hasn't been handled var exception = !context.ExceptionHandled ? context.Exception : null; PopulateResponseData( controller.ViewData.Concat(controller.TempData).ToDictionary(x => x.Key, x => x.Value), controller.ViewData.Model, exception); } /// /// The "main" object of this response. /// [DataMember(EmitDefaultValue = false)] public object Model { get; set; } /// /// Get the list of key-value pairs /// [DataMember(EmitDefaultValue = false)] public Dictionary Values { get; set; } #endregion #region Helpers private void PopulateResponseData(IDictionary values, object model, Exception exception) { var errorMessage = string.Empty; // If we've got an Exception in the context, try to use the error from that. if(exception != null) errorMessage = exception.Message; // Otherwise, try to glean the message from the View- and TempData. else if (values.ContainsKey(ERROR_MESSAGE_KEY)) { errorMessage = values[ERROR_MESSAGE_KEY] as string; values.Remove(ERROR_MESSAGE_KEY); } // Now set the rest of the data Model = model; Values = new Dictionary(values); IsError = !string.IsNullOrEmpty(errorMessage); // If we have an error, set the error message as our main message if (IsError) Message = errorMessage; // Otherwise, if we've got a success message, show that as our main message else if (Values.ContainsKey("SuccessMessage")) Message = values["SuccessMessage"] as string; } #endregion } } }