From 4b4034788cc1a89ebff8908495a88afe25fdea1c Mon Sep 17 00:00:00 2001 From: Yishai Galatzer Date: Mon, 3 Feb 2014 19:54:14 -0800 Subject: [PATCH] Initial support for JsonResult + Action result mutation from object to Json --- samples/MvcSample/Home2Controller.cs | 16 +++ .../ActionResultFactory.cs | 43 ++++++- .../ActionResults/JsonResult.cs | 112 ++++++++++++++++++ .../ControllerActionInvoker.cs | 3 +- 4 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc/ActionResults/JsonResult.cs diff --git a/samples/MvcSample/Home2Controller.cs b/samples/MvcSample/Home2Controller.cs index db272a3519..267b56afb6 100644 --- a/samples/MvcSample/Home2Controller.cs +++ b/samples/MvcSample/Home2Controller.cs @@ -1,10 +1,13 @@ using Microsoft.AspNet.Abstractions; using Microsoft.AspNet.Mvc; +using System.Text; namespace MvcSample { public class Home2Controller { + private User _user = new User() { Name = "User Name", Address = "Home Address" }; + public Home2Controller(IActionResultHelper actionResultHelper) { Result = actionResultHelper; @@ -36,5 +39,18 @@ namespace MvcSample { Context.Response.WriteAsync("Hello World raw"); } + + public IActionResult UserJson() + { + return new JsonResult(new User() { Name = "User Name", Address = "Home Address" }) + { + Encoding = Encoding.UTF8 + }; + } + + public User User() + { + return _user; + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc/ActionResultFactory.cs b/src/Microsoft.AspNet.Mvc/ActionResultFactory.cs index c4407eeed3..b54482d6e9 100644 --- a/src/Microsoft.AspNet.Mvc/ActionResultFactory.cs +++ b/src/Microsoft.AspNet.Mvc/ActionResultFactory.cs @@ -6,11 +6,46 @@ namespace Microsoft.AspNet.Mvc { public IActionResult CreateActionResult(Type declaredReturnType, object actionReturnValue, RequestContext requestContext) { - return new ContentResult + // optimize common path + IActionResult actionResult = actionReturnValue as IActionResult; + + if (actionResult != null) { - ContentType = "text/plain", - Content = Convert.ToString(actionReturnValue), - }; + return actionResult; + } + + if (declaredReturnType == null) + { + throw new InvalidOperationException("Declared type must be passed"); + } + + if (typeof(IActionResult).IsAssignableFrom(declaredReturnType) && actionReturnValue == null) + { + throw new InvalidOperationException("Cannot return null from an action method declaring IActionResult or HttpResponseMessage"); + } + + if (declaredReturnType.IsGenericParameter) + { + // This can happen if somebody declares an action method as: + // public T Get() { } + throw new InvalidOperationException("HttpActionDescriptor_NoConverterForGenericParamterTypeExists"); + } + + if (declaredReturnType.IsAssignableFrom(typeof(void))) + { + return new NoContentResult(); + } + + if (actionReturnValue is string) + { + return new ContentResult + { + ContentType = "text/plain", + Content = (string)actionReturnValue, + }; + } + + return new JsonResult(actionReturnValue); } } } diff --git a/src/Microsoft.AspNet.Mvc/ActionResults/JsonResult.cs b/src/Microsoft.AspNet.Mvc/ActionResults/JsonResult.cs new file mode 100644 index 0000000000..2fbeb85b65 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc/ActionResults/JsonResult.cs @@ -0,0 +1,112 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.Mvc +{ + public class JsonResult : IActionResult + { + private readonly object _returnValue; + + private JsonSerializerSettings _jsonSerializerSettings; + private Encoding _encoding; + + public JsonResult(object returnValue) + { + if (returnValue == null) + { + throw new ArgumentNullException("returnValue"); + } + + Encoding = Encoding.UTF8; + + _returnValue = returnValue; + } + + public JsonSerializerSettings SerializerSettings + { + get { return _jsonSerializerSettings; } + + set + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + + _jsonSerializerSettings = value; + } + } + + /// + /// Gets or sets a value indicating whether to indent elements when writing data. + /// + public bool Indent { get; set; } + + public Encoding Encoding + { + get { return _encoding; } + set + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + + _encoding = value; + } + } + + public virtual JsonSerializerSettings CreateSerializerSettings() + { + return new JsonSerializerSettings() + { + MissingMemberHandling = MissingMemberHandling.Ignore, + + // Do not change this setting + // Setting this to None prevents Json.NET from loading malicious, unsafe, or security-sensitive types. + TypeNameHandling = TypeNameHandling.None + }; + } + + public virtual JsonSerializer CreateJsonSerializer() + { + JsonSerializer jsonSerializer = JsonSerializer.Create(SerializerSettings); + + return jsonSerializer; + } + + public virtual JsonWriter CreateJsonWriter(Stream writeStream, Encoding effectiveEncoding) + { + JsonWriter jsonWriter = new JsonTextWriter(new StreamWriter(writeStream, effectiveEncoding)); + if (Indent) + { + jsonWriter.Formatting = Newtonsoft.Json.Formatting.Indented; + } + + return jsonWriter; + } + + public async Task ExecuteResultAsync(RequestContext context) + { + HttpResponse response = context.HttpContext.Response; + + Stream writeStream = response.Body; + + response.ContentType = "application/json"; + + using (JsonWriter jsonWriter = CreateJsonWriter(writeStream, Encoding)) + { + jsonWriter.CloseOutput = false; + + JsonSerializer jsonSerializer = CreateJsonSerializer(); + jsonSerializer.Serialize(jsonWriter, _returnValue); + + jsonWriter.Flush(); + } + } + } +} diff --git a/src/Microsoft.AspNet.Mvc/ControllerActionInvoker.cs b/src/Microsoft.AspNet.Mvc/ControllerActionInvoker.cs index 5f1f59afd3..c5c7dcc599 100644 --- a/src/Microsoft.AspNet.Mvc/ControllerActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc/ControllerActionInvoker.cs @@ -47,8 +47,7 @@ namespace Microsoft.AspNet.Mvc object actionReturnValue = method.Invoke(controller, null); - // If it's not already an IActionResult then call into the factory - var actionResult = actionReturnValue as IActionResult ?? _actionResultFactory.CreateActionResult(method.ReturnType, actionReturnValue, _requestContext); + var actionResult = _actionResultFactory.CreateActionResult(method.ReturnType, actionReturnValue, _requestContext); return actionResult.ExecuteResultAsync(_requestContext); }