I'm having problems when I try to deserialize json into a object in MVC4.
I have a Viewmodel:
public class TestViewModel
{
public string Code { get; set; }
}
On the view I get the model and serialize the object using Json.net
var Vm = function(data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
self.GetResults = function() {
$.ajax({
type: "POST",
url: '#Url.Action("Action", "Controller")',
data: ko.mapping.toJSON(self),
success: function(data) {
alert('OK');
}
});
};
};
var viewModel = new Vm(#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model)));
ko.applyBindings(viewModel);
My problem is when I call GetResults the action in the controller, all properties are null.
My Json is:
{"Code":"TestCode"}
I have the same structure in a MVC3 project and works fine. I'm missing something in MVC4?
Cheers!
We've noticed that in some scenarios jQuery will embed the data in a Form in the Request. When this happens, the values are not automatically mapped to the object type in the Controller method.
To get around this, you need to do two things:
1) Check to see if the data is serialized. I found an easy way to do this and dumped it in an extension method:
public static class WebContextExtensions
{
public static bool IsDataSerialized(this HttpContext context)
{
return context.Request.Params.AllKeys[0] == null;
}
}
2) IF IsDataSerialized returns true, you need to deserialize the data into your object type. We wrote a GenericDeserializer method to do that as well:
public class GenericContextDeserializer<T> where T : new()
{
public T DeserializeToType(HttpContext context)
{
T result = new T();
if (context != null)
{
try
{
string jsonString = context.Request.Form.GetValues(0)[0].ToString();
Newtonsoft.Json.JsonSerializer js = new Newtonsoft.Json.JsonSerializer();
result = js.Deserialize<T>(new Newtonsoft.Json.JsonTextReader(
new System.IO.StringReader(jsonString)));
}
catch (Exception ex)
{
throw ex;
}
}
else
throw new NullReferenceException();
return result;
}
}
Now put it all together in your Controller method:
[HttpPost]
[HttpOptions]
public HttpResponseMessage MyAction(JsonData data)
{
var results = Request.CreateResponse();
try
{
data = new GenericContextDeserializer<JsonData>().DeserializeToType(HttpContext.Current);
// do stuff with data
results.StatusCode = HttpStatusCode.OK;
}
catch (Exception ex)
{
results.StatusCode = HttpStatusCode.InternalServerError;
}
return results;
}
If you want more detail, it's in the second half of a blog post I wrote.
Related
I am calling and displaying a java web service using ASP.NET Web API. How do i make it such that when i run my ASP.NET Web API, the page shows JSON data instead of HTML?
Here are my codes:
DemoRestfulClient.cs
public class DemoRestfulClient
{
private string BASE_URL = "http://localhost:8080/";
public Task<string> AdditionJava2()
{
{
try
{
var client = new HttpClient();
client.BaseAddress = new Uri(BASE_URL);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("AdditionJava2").Result;
return response.Content.ReadAsStringAsync();
}
catch (Exception e)
{
HttpContext.Current.Server.Transfer("ErrorPage.html");
}
return null;
}
}
}
DemoController.cs
public class DemoController : Controller
{
private DemoRestfulClient demoRestfulClient = new DemoRestfulClient();
public ActionResult Index()
{
var Result1 = demoRestfulClient.AdditionJava2().Result;
return Content(Result1);
}
}
Someone please help me. Thank you so much in advance.
public class DemoController : Controller
{
private DemoRestfulClient demoRestfulClient = new DemoRestfulClient();
public ActionResult Index()
{
var Result1 = demoRestfulClient.AdditionJava2().Result;
return Json(Result1);
}
}
The above method will return a json object .
You have wanted to get the json object, right? :)
You have to parse the Json object in order to separately view the content in json.
By using ajax, you can get the content of the json object separately.
For an example,
$.ajax({
url: $("#head").val() + "/Template/updatedTmpltView",
dataType: "html",
data: {},
type: "POST",
success: function (msg) {
data = $.parseJSON(msg)
var name = data.FieldName;
var type = data.FieldType;
var id = data.FieldId;
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
}
});
In the success (msg), you get the json object as **msg**.
data will include the parsed json object and you can obtain necessary data by data.yourFieldName
Hope this helped you! :)
cannot convert from 'System.Web.Mvc.JsonRequestBehavior' to 'Newtonsoft.Json.JsonSerializerSettings'
code
public JsonResult Get()
{
try
{
using (smartpondEntities DB = new smartpondEntities())
{
var pond = DB.Temperatures.OrderByDescending(x => x.WaterTemperature).FirstOrDefault();
return Json(new { success = true, sensorsdata = new { id = pond.WaterTemperature, CurrentTime = pond.CreatedDate } }, JsonRequestBehavior.AllowGet);
}
}
catch (Exception Ex)
{
}
return Json(new { success = false }, JsonRequestBehavior.AllowGet);
}
The second parameter for Json method in Web API controller is incorrectly assigned, since ApiController Json method requires JsonSerializerSettings as second argument:
protected internal JsonResult<T> Json<T>(T content, JsonSerializerSettings serializerSettings)
{
......
}
The MVC controller counterpart for Json method is shown below:
protected internal JsonResult Json(object data, JsonRequestBehavior behavior)
{
......
}
In this case, if the controller class containing Get method above extends ApiController, you need to change 2 return Json statements to return new JsonResult as given below:
public class ControllerName : ApiController
{
public JsonResult Get()
{
try
{
using (smartpondEntities DB = new smartpondEntities())
{
var pond = DB.Temperatures.OrderByDescending(x => x.WaterTemperature).FirstOrDefault();
// return JsonResult here
return new JsonResult()
{
Data = new { success = true, sensorsdata = new { id = pond.WaterTemperature, CurrentTime = pond.CreatedDate }},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
catch (Exception Ex)
{
}
// return JsonResult here
return new JsonResult()
{
Data = new { success = false },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
If you want to use MVC controller when returning JSON instead, change ApiController to Controller class from System.Web.Mvc namespace and keep return Json(...) there.
Similar issue:
JSON return error with ASP
I am doing From dropdown selection, I fill textboxes with the Json Value. Action() Method work fine It return Json value but page not populate these value to TextBoxes control. When i use Developer Tool then i get, it throw a Error "The operation cannot be completed because the DbContext has been disposed."
Controller
private hcEntities db = new hcEntities();
// GET: Chains
public ActionResult Index()
{
ViewData["chain_name"] = new SelectList(db.chains, "code", "name");
return View(db.chains.ToList());
}
//Action Function callby javascript
[HttpPost]
public ActionResult Action(string code)
{
using (var ObjDb = new hcEntities())
{
var query = from c in ObjDb.chains
where c.code == code
select c;
return Json(query);//Return Json Result
}
}
View:-
<script type="text/javascript">
function Action(code) {
$.ajax({
url: '#Url.Action("Action", "Chains")',
type: "POST",
data: { "code": code },
"success": function (data) {
if (data != null) {
var vdata = data;
$("#ChainName").val(vdata[0].name);
$("#ChainCode").val(vdata[0].code);
$("#username").val(vdata[0].username);
}
}
});
}
Try return Json(query.FirstOrDefault());
Use IDisposable again in the GET Controller:
// GET: Chains
public ActionResult Index()
{
using (var ObjDb = new hcEntities())
{
ViewData["chain_name"] = new SelectList(ObjDb.chains, "code", "name");
return View(ObjDb.chains.ToList());
}
}
I have a form on a webpage that I submit using the jquery form plugin. This form includes a file upload option. The MVC3 action that I post to returns JSON. Since the plugin falls back to using an iframe on older browsers you need to wrap your JSON with a
<textarea>JSON data...</textarea>
I tried changing the return type of the action to string and just appending the text area tags the the JSON object.ToString() but no go. How can I wrap my JSON result in a textarea when !Request.IsAjaxRequest()
Here is an example of me just trying to return the JSON as a string (which doesn't work)
[HttpPost]
public string CreateEntry(EntryCreateViewModel model)
{
if (!ModelState.IsValid)
{
return WrapInTextArea(!Request.IsAjaxRequest(), Json(new object[] { false, 0, this.RenderPartialViewToString("_EntryCreateFormPartial", model) }).ToString());
}
This works in modern browsers but I suspect (based on the docs) will fail in older browsers that use an iframe
[HttpPost]
public ActionResult CreateEntry(EntryCreateViewModel model)
{
if (!ModelState.IsValid)
{
return Json(new object[] {false, 0, this.RenderPartialViewToString("_EntryCreateFormPartial", model)});
}
To solve this I inherited from JsonResult and added the textarea there
using System;
using System.Web.Mvc;
using System.Web.Script.Serialization;
namespace TinyHouseMap.Web.Infrastructure.Results
{
public class JsonInIframeResult : JsonResult
{
public bool EncloseInTextArea
{
get;
set;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if ((JsonRequestBehavior == JsonRequestBehavior.DenyGet)
&& string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("JsonRequest GetNotAllowed");
}
var response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(ContentType) ? ContentType : "application/json";
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
}
if (Data != null)
{
var serializer = new JavaScriptSerializer();
string results;
if (EncloseInTextArea)
{
results = "<textarea>" + serializer.Serialize(Data) + "</textarea>";
}
else
{
results = serializer.Serialize(Data);
}
response.Write(results);
}
}
}
}
And then created a helper in my controller base class
protected JsonInIframeResult JsonInIframe(object data, string contentType, bool encloseInTextArea)
{
var result = new JsonInIframeResult {Data = data, ContentType = contentType, EncloseInTextArea = encloseInTextArea};
return result;
}
I am looking to return some JSON across domains and I understand that the way to do this is through JSONP rather than pure JSON.
I am using ASP.net MVC so I was thinking about just extending the JsonResult type and then extending the Controller so that it also implemented a Jsonp method.
Is this the best way to go about it or is there a built-in ActionResult that might be better?
Solution: I went ahead and did that. Just for reference sake I added a new result:
public class JsonpResult : System.Web.Mvc.JsonResult
{
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
else
{
response.ContentType = "application/javascript";
}
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
}
if (Data != null)
{
// The JavaScriptSerializer type was marked as obsolete prior to .NET Framework 3.5 SP1
#pragma warning disable 0618
HttpRequestBase request = context.HttpContext.Request;
JavaScriptSerializer serializer = new JavaScriptSerializer();
response.Write(request.Params["jsoncallback"] + "(" + serializer.Serialize(Data) + ")");
#pragma warning restore 0618
}
}
}
and also a couple of methods to a superclass of all my controllers:
protected internal JsonpResult Jsonp(object data)
{
return Jsonp(data, null /* contentType */);
}
protected internal JsonpResult Jsonp(object data, string contentType)
{
return Jsonp(data, contentType, null);
}
protected internal virtual JsonpResult Jsonp(object data, string contentType, Encoding contentEncoding)
{
return new JsonpResult
{
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding
};
}
Works like a charm.
Here is a simple solution, if you don't want to define an action filter
Client side code using jQuery:
$.ajax("http://www.myserver.com/Home/JsonpCall", { dataType: "jsonp" }).done(function (result) {});
MVC controller action. Returns content result with JavaScript code executing callback function provided with query string. Also sets JavaScript MIME type for response.
public ContentResult JsonpCall(string callback)
{
return Content(String.Format("{0}({1});",
callback,
new JavaScriptSerializer().Serialize(new { a = 1 })),
"application/javascript");
}
Rather than subclassing my controllers with Jsonp() methods, I went the extension method route as it feels a touch cleaner to me. The nice thing about the JsonpResult is that you can test it exactly the same way you would a JsonResult.
I did:
public static class JsonResultExtensions
{
public static JsonpResult ToJsonp(this JsonResult json)
{
return new JsonpResult { ContentEncoding = json.ContentEncoding, ContentType = json.ContentType, Data = json.Data, JsonRequestBehavior = json.JsonRequestBehavior};
}
}
This way you don't have to worry about creating all the different Jsonp() overloads, just convert your JsonResult to a Jsonp one.
Ranju's blog post (aka "This blog post I found") is excellent, and reading it will allow you to further the solution below so that your controller can handle same-domain JSON and cross-domain JSONP requests elegantly in the same controller action without additional code [in the action].
Regardless, for the "give me the code" types, here it is, in case the blog disappears again.
In your controller (this snippet is new/non-blog code):
[AllowCrossSiteJson]
public ActionResult JsonpTime(string callback)
{
string msg = DateTime.UtcNow.ToString("o");
return new JsonpResult
{
Data = (new
{
time = msg
})
};
}
JsonpResult found on
this excellent blog post:
/// <summary>
/// Renders result as JSON and also wraps the JSON in a call
/// to the callback function specified in "JsonpResult.Callback".
/// http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx
/// </summary>
public class JsonpResult : JsonResult
{
/// <summary>
/// Gets or sets the javascript callback function that is
/// to be invoked in the resulting script output.
/// </summary>
/// <value>The callback function name.</value>
public string Callback { get; set; }
/// <summary>
/// Enables processing of the result of an action method by a
/// custom type that inherits from <see cref="T:System.Web.Mvc.ActionResult"/>.
/// </summary>
/// <param name="context">The context within which the
/// result is executed.</param>
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
response.ContentType = ContentType;
else
response.ContentType = "application/javascript";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
if (Callback == null || Callback.Length == 0)
Callback = context.HttpContext.Request.QueryString["callback"];
if (Data != null)
{
// The JavaScriptSerializer type was marked as obsolete
// prior to .NET Framework 3.5 SP1
#pragma warning disable 0618
JavaScriptSerializer serializer = new JavaScriptSerializer();
string ser = serializer.Serialize(Data);
response.Write(Callback + "(" + ser + ");");
#pragma warning restore 0618
}
}
}
Note: Following up on the comments to the OP by #Ranju and others, I figured it was worth posting the "bare minimum" functional code from Ranju's blog post as a community wiki. Though it's safe to say that Ranju added the above and other code on his blog to be used freely, I'm not going to copy his words here.
For ASP.NET Core ,NOT ASP.NET MVC
This is a tailored version for ASP.NET CORE of the solution which exists in the answer
public class JsonpResult : JsonResult
{
public JsonpResult(object value) : base(value)
{
}
public override async Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
HttpResponse response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
response.ContentType = ContentType;
else
response.ContentType = "application/javascript";
if (Value != null)
{
HttpRequest request = context.HttpContext.Request;
string serializedJson = JsonConvert.SerializeObject(Value);
string result = $"{request.Query["callback"]}({serializedJson})";
await response.WriteAsync(result);
}
}
}
The referenced articles by stimms and ranju v were both very useful and made the situation clear.
However, I was left scratching my head about using extensions, sub-classing in context of the MVC code I had found online.
There was two key points that caught me out:
The code I had derived from ActionResult, but in ExecuteResult there was some code to return either XML or JSON.
I had then created a Generics based ActionResult, to ensure the same ExecuteResults was used independant of the type of data I returned.
So, combining the two - I did not need further extensions or sub-classing to add the mechanism to return JSONP, simply change my existing ExecuteResults.
What had confused me is that really I was looking for a way to derive or extend JsonResult, without re-coding the ExecuteResult. As JSONP is effectively a JSON string with prefix & suffix it seemed a waste. However the underling ExecuteResult uses respone.write - so the safest way of changing is to re-code ExecuteResults as handily provided by various postings!
I can post some code if that would be useful, but there is quite a lot of code in this thread already.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
namespace Template.Web.Helpers
{
public class JsonpResult : JsonResult
{
public JsonpResult(string callbackName)
{
CallbackName = callbackName;
}
public JsonpResult()
: this("jsoncallback")
{
}
public string CallbackName { get; set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
var request = context.HttpContext.Request;
var response = context.HttpContext.Response;
string jsoncallback = ((context.RouteData.Values[CallbackName] as string) ?? request[CallbackName]) ?? CallbackName;
if (!string.IsNullOrEmpty(jsoncallback))
{
if (string.IsNullOrEmpty(base.ContentType))
{
base.ContentType = "application/x-javascript";
}
response.Write(string.Format("{0}(", jsoncallback));
}
base.ExecuteResult(context);
if (!string.IsNullOrEmpty(jsoncallback))
{
response.Write(")");
}
}
}
public static class ControllerExtensions
{
public static JsonpResult Jsonp(this Controller controller, object data, string callbackName = "callback")
{
return new JsonpResult(callbackName)
{
Data = data,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
public static T DeserializeObject<T>(this Controller controller, string key) where T : class
{
var value = controller.HttpContext.Request.QueryString.Get(key);
if (string.IsNullOrEmpty(value))
{
return null;
}
JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
return javaScriptSerializer.Deserialize<T>(value);
}
}
}
//Example of using the Jsonp function::
// 1-
public JsonResult Read()
{
IEnumerable<User> result = context.All();
return this.Jsonp(result);
}
//2-
public JsonResult Update()
{
var models = this.DeserializeObject<IEnumerable<User>>("models");
if (models != null)
{
Update(models); //Update properties & save change in database
}
return this.Jsonp(models);
}
the solution above is a good way of working but it should be extendend with a new type of result instead of having a method that returns a JsonResult you should write methods that return your own result types
public JsonPResult testMethod() {
// use the other guys code to write a method that returns something
}
public class JsonPResult : JsonResult
{
public FileUploadJsonResult(JsonResult data) {
this.Data = data;
}
public override void ExecuteResult(ControllerContext context)
{
this.ContentType = "text/html";
context.HttpContext.Response.Write("<textarea>");
base.ExecuteResult(context);
context.HttpContext.Response.Write("</textarea>");
}
}