The JsonResult class is a very useful way to return Json as an action to the client via AJAX.
public JsonResult JoinMailingList(string txtEmail) { // ... return new JsonResult() { Data = new { foo = "123", success = true } }; }
However (at least according to my first impression) this really isn't a good separation of concerns.
I'm wondering why the translation between an object and Json wasn't implemented declaratively via an attribute. In the code below you're essentially telling MVC that this method is convertible to Json
, and then if it is called from an AJAX client a check is made for the attribute the new JsonResult()
conversion performed internally.
Unit testing can just take the action result (ObjectActionResult
) and pull out the strongly typed Foo
.
[JsonConvertible] public ActionResult JoinMailingList(string txtEmail) { // ... return new ObjectActionResult() { Data = new Foo(123, true) }; }
I was just curious as to people's thoughts and any alternative pattern to follow.
These are also just my initial observations - there are probably more reasons why this is not an ideal design (and probably plenty why its a perfectly acceptable and practical one!) I'm just feeling theoretical and devils-advocatey tonight.
* Disclaimer: I haven't even begun to think about how the attribute would be implemented or what sideeffects or repurcussions etc. it might have.
"JSON" (JavaScript Object Notation) is a lightweight text-based open standard designed for human-readable data interchange. When working together with "jQuery" and "ASP.NET MVC" in building web applications, it provides an efficient mechanism to exchange data between the web browser and the web server.
What is JsonResult ? JsonResult is one of the type of MVC action result type which returns the data back to the view or the browser in the form of JSON (JavaScript Object notation format).
The JSON format is an open standard format. The format of data looks very easy to understand and the data objects consist of attribute-value pairs. ContentEncoding: It helps to indicate the content encoding type, the default encoding for JSON is UTF-8. ContentType: It helps to indicate the content type.
I think you're getting worked up over nothing. So what if the controller knows about JSON in its public interface?
I was once told: "Make your code generic, don't make your application generic."
You're writing an Application Controller here. It's OK for the Application Controller - whose responsibility is to mitigate between the model and views and to invoke changes in the model - to know about a certain view (JSON, HTML, PList, XML, YAML).
In my own projects, I usually have something like:
interface IFormatter { ActionResult Format(object o); } class HtmlFormatter : IFormatter { // ... } class JsonFormatter : IFormatter { // ... } class PlistFormatter : IFormatter { // ... } class XmlFormatter : IFormatter { // ... }
Basically "formatters" that take objects and give them a different representation. The HtmlFormatter
s are even smart enough to output tables if their object implements IEnumerable
.
Now the controllers that return data (or that can generate parts of the website using HtmlFormatter
s) take a "format" argument:
public ActionResult JoinMailingList(string txtEmail, string format) { // ... return Formatter.For(format).Format( new { foo = "123", success = true } ); }
You could add your "object" formatter for your unit tests:
class ObjectFormatter : IFormatter { ActionResult Format(object o) { return new ObjectActionResult() { Data = o }; } }
Using this methodology, any of your queries/actions/procedures/ajax calls, whatever you want to call them, can output in a variety of formats.
I generally try not to worry about it. The Asp.Net MVC is enough of a separation of concerns to keep leakage to a minimum. You're right though; there is a bit of a hurdle when testing.
Here's a test helper I use, and it's worked well:
protected static Dictionary<string, string> GetJsonProps(JsonResult result) { var properties = new Dictionary<string, string>(); if (result != null && result.Data != null) { object o = result.Data; foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(o)) properties.Add(prop.Name, prop.GetValue(o) as string); } return properties; }
You can use the Request.IsAjaxRequest() extension method to return different ActionResult types:
if (this.Request != null && this.Request.IsAjaxRequest()) return Json(new { Message = "Success" }); else return RedirectToAction("Some Action");
Note: you'll need that Request != null to not break your tests.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With