I am trying to create controller actions which will return either JSON or partial html depending upon a parameter. What is the best way to get the result returned to an MVC page asynchronously?
In your action method, return Json(object) to return JSON to your page.
public ActionResult SomeActionMethod() {
return Json(new {foo="bar", baz="Blech"});
}
Then just call the action method using Ajax. You could use one of the helper methods from the ViewPage such as
<%= Ajax.ActionLink("SomeActionMethod", new AjaxOptions {OnSuccess="somemethod"}) %>
SomeMethod would be a javascript method that then evaluates the Json object returned.
If you want to return a plain string, you can just use the ContentResult:
public ActionResult SomeActionMethod() {
return Content("hello world!");
}
ContentResult by default returns a text/plain as its contentType.
This is overloadable so you can also do:
return Content("<xml>This is poorly formatted xml.</xml>", "text/xml");
I think you should consider the AcceptTypes of the request. I am using it in my current project to return the correct content type as follows.
Your action on the controller can test it as on the request object
if (Request.AcceptTypes.Contains("text/html")) {
return View();
}
else if (Request.AcceptTypes.Contains("application/json"))
{
return Json( new { id=1, value="new" } );
}
else if (Request.AcceptTypes.Contains("application/xml") ||
Request.AcceptTypes.Contains("text/xml"))
{
//
}
You can then implement the aspx of the view to cater for the partial xhtml response case.
Then in jQuery you can fetch it passing the type parameter as json:
$.get(url, null, function(data, textStatus) {
console.log('got %o with status %s', data, textStatus);
}, "json"); // or xml, html, script, json, jsonp or text
Another nice way to deal with JSON data is using the JQuery getJSON function. You can call the
public ActionResult SomeActionMethod(int id)
{
return Json(new {foo="bar", baz="Blech"});
}
Method from the jquery getJSON method by simply...
$.getJSON("../SomeActionMethod", { id: someId },
function(data) {
alert(data.foo);
alert(data.baz);
}
);
I found a couple of issues implementing MVC ajax GET calls with JQuery that caused me headaches so sharing solutions here.
Make sure to include the data type "json" in the ajax call. This will automatically parse the returned JSON object for you (given the server returns valid json).
Include the JsonRequestBehavior.AllowGet; without this MVC was returning a HTTP 500 error (with dataType: json specified on the client).
Add cache: false to the $.ajax call, otherwise you will ultimately get HTTP 304 responses (instead of HTTP 200 responses) and the server will not process your request.
Finally, the json is case sensitive, so the casing of the elements needs to match on the server side and client side.
Sample JQuery:
$.ajax({
type: 'get',
dataType: 'json',
cache: false,
url: '/MyController/MyMethod',
data: { keyid: 1, newval: 10 },
success: function (response, textStatus, jqXHR) {
alert(parseInt(response.oldval) + ' changed to ' + newval);
},
error: function(jqXHR, textStatus, errorThrown) {
alert('Error - ' + errorThrown);
}
});
Sample MVC code:
[HttpGet]
public ActionResult MyMethod(int keyid, int newval)
{
var oldval = 0;
using (var db = new MyContext())
{
var dbRecord = db.MyTable.Where(t => t.keyid == keyid).FirstOrDefault();
if (dbRecord != null)
{
oldval = dbRecord.TheValue;
dbRecord.TheValue = newval;
db.SaveChanges();
}
}
return Json(new { success = true, oldval = oldval},
JsonRequestBehavior.AllowGet);
}
To answer the other half of the question, you can call:
return PartialView("viewname");
when you want to return partial HTML. You'll just have to find some way to decide whether the request wants JSON or HTML, perhaps based on a URL part/parameter.
Alternative solution with incoding framework
Action return json
Controller
[HttpGet]
public ActionResult SomeActionMethod()
{
return IncJson(new SomeVm(){Id = 1,Name ="Inc"});
}
Razor page
#using (var template = Html.Incoding().ScriptTemplate<SomeVm>("tmplId"))
{
using (var each = template.ForEach())
{
<span> Id: #each.For(r=>r.Id) Name: #each.For(r=>r.Name)</span>
}
}
#(Html.When(JqueryBind.InitIncoding)
.Do()
.AjaxGet(Url.Action("SomeActionMethod","SomeContoller"))
.OnSuccess(dsl => dsl.Self().Core()
.Insert
.WithTemplate(Selector.Jquery.Id("tmplId"))
.Html())
.AsHtmlAttributes()
.ToDiv())
Action return html
Controller
[HttpGet]
public ActionResult SomeActionMethod()
{
return IncView();
}
Razor page
#(Html.When(JqueryBind.InitIncoding)
.Do()
.AjaxGet(Url.Action("SomeActionMethod","SomeContoller"))
.OnSuccess(dsl => dsl.Self().Core().Insert.Html())
.AsHtmlAttributes()
.ToDiv())
You may want to take a look at this very helpful article which covers this very nicely!
Just thought it might help people searching for a good solution to this problem.
http://weblogs.asp.net/rashid/archive/2009/04/15/adaptive-rendering-in-asp-net-mvc.aspx
PartialViewResult and JSONReuslt inherit from the base class ActionResult. so if return type is decided dynamically declare method output as ActionResult.
public ActionResult DynamicReturnType(string parameter)
{
if (parameter == "JSON")
return Json("<JSON>", JsonRequestBehavior.AllowGet);
else if (parameter == "PartialView")
return PartialView("<ViewName>");
else
return null;
}
For folks who have upgraded to MVC 3 here is a neat way
Using MVC3 and Json
public ActionResult GetExcelColumn()
{
List<string> lstAppendColumn = new List<string>();
lstAppendColumn.Add("First");
lstAppendColumn.Add("Second");
lstAppendColumn.Add("Third");
return Json(new { lstAppendColumn = lstAppendColumn, Status = "Success" }, JsonRequestBehavior.AllowGet);
}
}
Flexible approach to produce different outputs based on the request
public class AuctionsController : Controller
{
public ActionResult Auction(long id)
{
var db = new DataContext();
var auction = db.Auctions.Find(id);
// Respond to AJAX requests
if (Request.IsAjaxRequest())
return PartialView("Auction", auction);
// Respond to JSON requests
if (Request.IsJsonRequest())
return Json(auction);
// Default to a "normal" view with layout
return View("Auction", auction);
}
}
The Request.IsAjaxRequest() method is quite simple: it merely checks the HTTP headers for the incoming request to see if the value of the X-Requested-With header is XMLHttpRequest, which is automatically appended by most browsers and AJAX frameworks.
Custom extension method to check whether the request is for json or not so that we can call it from anywhere, just like the Request.IsAjaxRequest() extension method:
using System;
using System.Web;
public static class JsonRequestExtensions
{
public static bool IsJsonRequest(this HttpRequestBase request)
{
return string.Equals(request["format"], "json");
}
}
Source : https://www.safaribooksonline.com/library/view/programming-aspnet-mvc/9781449321932/ch06.html#_javascript_rendering
Related
I have a function that triggers on a button click that passes the following argument: insertSongPlay(newSong.songID); when I console.log the newSong.songID I see a value of 90, which is desired.
This function is called here which runs an ajax call:
function insertSongPlay(songID)
{
$.ajax
({
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
url: "/Album/InsertSongPlay/",
data: JSON.stringify({ "songID": songID }),
success: function(data)
{
console.log("submitted");
console.log(songID);
//TODO: Indicate Success
},
error: function(jqXHR, textStatus, errorThrown)
{
//TODO: Indicate Error
console.log(errorThrown);
}
});
}
Here is the corresponding ajax url found in my AlbumController:
public JsonResult InsertSongPlay(int songID)
{
try
{
EntityDataAccess.InsertSongPlay(songID);
return Json(true);
}
catch(Exception ex)
{
return Json(ex);
}
}
Then in my EntityDataAccess.cs the following is run from the InsertSongPlay entity data access:
public static SongPlayDaily InsertSongPlay(int songID)
{
using(var Context = GetContext())
{
var currentSongPlay = Context.SongPlayDailies.FirstOrDefault(x => x.SongID == songID && x.PlayDate == DateTime.Now.Date);
if (currentSongPlay != null)
currentSongPlay.NumberOfPlays++;
else
{
currentSongPlay = new SongPlayDaily();
currentSongPlay.SongID = songID;
currentSongPlay.PlayDate = DateTime.Now.Date;
currentSongPlay.NumberOfPlays = 1;
Context.SongPlayDailies.Add(currentSongPlay);
}
Context.SaveChanges();
return currentSongPlay;
}
}
However, my ajax call always throws an error and prints Internal Server Error to the console log. The Visual Studio debug mode indicates some sort of circular reference error. I'm not quite sure where I'm going wrong.
Thanks!
As we figured out in comments - there are multiple problems here. First,
return Json(ex);
Is a bad idea. That will inspect all properties of exception and try to serialize them, and all properties of those properties, to json. As you see, that is not going well, and also completely unnecessary. If you want to return error response to client - prepare it (use ex.Message for example). Plus, you don't want to return sensetive information (like stack trace) to your clients on every error.
Besides that - by doing return Json() on exception - you violate HTTP conventions. Json() will return response with status code 200 OK. But in your case it's not really "ok" - so you should return another status code indicating error. If not that circular reference error - your code in ajax "error" handler would never has been triggered, even when there was really an error.
Easiest way to fix it is just remove try-catch completely and let framework handle that for you.
Second problem is
x.PlayDate == DateTime.Now.Date
filter in query. Entity Framework has problem with accessing Date property of DateTime objects and refuses to convert that into SQL query. You can either move that to another variable:
var today = DateTime.Now.Date;
Where(x => x.PlayDate == today)
or use DbFunctions.TruncateTime:
Where(x => x.PlayDate == DbFunctions.TruncateTime(DateTime.Now))
Note that those ways are not exactly equivalent. First will compare with exact date value, and second will use SQL SYSDATETIME() function. In this case that hardly matters though.
Is it because you're missing the [HttpPost] attribute over your controller action?
[HttpPost]
public JsonResult InsertSongPlay(int songID)
{
try
{
EntityDataAccess.InsertSongPlay(songID);
return Json(true);
}
catch(Exception ex)
{
return Json(ex);
}
}
in post action you should use complex type for receive data from client.
try this
[HttpPost]
public JsonResult InsertSongPlay(SongModel songModel)
{
try
{
EntityDataAccess.InsertSongPlay(songModel.songID);
return Json(true);
}
catch(Exception ex)
{
return Json(ex);
}
}
public class SongModel{
public int songID {get;set}
}
I am using a DDL in my index page, I get to the action I want, but nothing happens there although my code is right (I mean the result back is like null). But I don't know how or what to do to get data back from my action although I get a success back (when I try it with some alert notion).
This is my index.cshtml code:
$("#SjukhusNamn").change(function () {
$.ajax(
{
type: "POST",
data: { sjukhus: $(this).val(#Model.SjukhusNamn) },
success: function (sjukhus) {
alert("success");
},
url: "#Url.Action("Index", "Home")"
});
});
In my action, a simple [HttpPost] and then
public ActionResult Index(VardplatsOversiktViewModel model, string sjukhus)
{
if (!string.IsNullOrEmpty(sjukhus))
{
model.SjukhusNamn = sjukhus;
model.ListaVardplatsOversikt = db.tblVardplatsOversikt
.Where(s => s.Sjukhus == model.SjukhusNamn)
.ToList();
return View(model);
}
}
sjukhus is hospital in english.
The easiest way would be to convert your Object to JSON, then write it to the response. I like to use the jackson library for this, but there are others. Once you have the Object as a json String, get a PrintWriter and write it to the response with the correct content type:
PrintWriter out = null;
logger.info("Replying to client = " + jsonStr);
try {
out = response.getWriter();
response.setContentType("text/json;charset=utf-8");
out.print(jsonStr);
} catch (IOException e) {
logger.error("Error sending response to client: " + e);
}
What I want is, I want to check whether there is a file in the database or not. To do this I have a method in the controller which checks this and returns a boolean for the corresponding case. It looks like this:
public bool fileInDb(int empId)
{
using (SLADbContext db = new SLADbContext())
{
bool file = db.CompetenceUploads.Any(x => x.EmployeeId == empId);
if (file)
{
return true;
}
else
{
return false;
}
}
}
I simply just check if there is any file assigned to the given employee.
Now I would like to call this method from my javascript in the view, and get the return value, so that I can let the user know, if there is a file assigned to the selected employee or not. It may look like this:
$("#get-file").click(function() {
empId: $("#EmployeeSelect").val();
var fileInDb = // Get the return value from the method 'fileInDb'
if(fileInDb) {
// Let the user download the file he/she requested
var url = "#Url.Action("GetUploadedFile", "Competence")";
this.href = url + '?empId=' + encodeURIComponent($("#EmployeeSelect").val());
} else {
alert("There is no file assigned to this employee.");
}
});
So my question now is, how do I get the get the return value from the method in the controller?
I would suggest few changes here:
Change your controller method to have return type ActionResult or JsonResult and I prefer JsonResult would be enough here and retrun Json response from controller and manipulate this method with $.get. You also need to change parameter to string because the parameter will be received as Json string.
public JsonResult fileInDb(string eId) //change signature to string and then convert to int
{
int empId=Convert.ToInt32(eId);
using (SLADbContext db = new SLADbContext())
{
bool file = db.CompetenceUploads.Any(x => x.EmployeeId == empId);
if (file)
{
return Json(new { result = true },JsonRequestBehavior.AllowGet);
}
else
{
return Json(new { result = false},JsonRequestBehavior.AllowGet);
}
}
}
Now your ajax-get call would be as below:
$("#get-file").click(function() {
var eId= $("#EmployeeSelect").val();
$.get('/YourControllerName/fileInDb',{'eId':eId},function(response){
//you just need to get the response so $.get is enough to manipulate
//this will be called once you get the response from controller, typically a callback
if(response.result) //same result variable we are returning from controller.
{
// Let the user download the file he/she requested
var url = "#Url.Action("GetUploadedFile", "Competence")";
this.href = url + '?empId=' + encodeURIComponent($("#EmployeeSelect").val());
} else {
alert("There is no file assigned to this employee.");
}
})
});
You need to set-up a single page script using your ASP fileInDb function and then communicate with that page using AJAX from the browser. If you're unfamiliar with AJAX I'd recommend using the jQuery implementation to get you started.
You can use jquery and ajax to achieve this. Call your method using an ajax call from your client code. Here is an example as a reference :Calling controller method from view
In the backend create a method to call, returning a JsonResult
public JsonResult fileInDb(int empId)
{
// your code - set fileExists to true/false
JsonResult returnObj = new JsonResult
{
Data = new
{
FileExists = fileExists ;
}
};
return Json(returnObj);
}
in your javascript code use $.ajax
$.ajax({
cache: false,
url: '#Url.Action("fileInDb")',
data: { 'empId': someVar },
type: 'POST',
success: function (response) {
if (response.Data.FileExists === true) {
// do something
} else {
// it was false
}
},
error: function (er) {
alert('Error!' + er);
}
});
I have this code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(BMICalculation CalculateBMI)
{
if (ModelState.IsValid)
{
CalculateBMI.Id = User.Identity.GetUserId();
CalculateBMI.Date = System.DateTime.Now;
CalculateBMI.BMICalc = CalculateBMI.CalculateMyBMI(CalculateBMI.Weight, CalculateBMI.Height);
CalculateBMI.BMIMeaning = CalculateBMI.BMIInfo(CalculateBMI.BMICalc);
ShowBMI(CalculateBMI.ToString());
db.BMICalculations.Add(CalculateBMI);
db.SaveChanges();
}
return View();
}
public JsonResult ShowBMI(string BMICalculation){
BMICalculation BMI = new BMICalculation();
var data = new
{
BMICalculation
};
return Json(data, JsonRequestBehavior.AllowGet);
}
I want the BMI calculation to be passed from the Create(BMICalculation CalculateBMI) method to the ShowBMI method so that it can be then passed to my webpage as a Json value.
The javascript function is called by submit button (which also processes the form):
<button type="submit" id="submit" onclick="ShowBMI();"> <span class="glyphicon glyphicon-floppy-save"></span></button>
And my JS is
<script type="text/javascript">
$(function ShowBMI() {
$.getJSON('/BMICalculations/ShowBMI', function (data) {
alert(data.BMICalculation);
});
});
</script>
When I click the submit button an alert appears saying null even though I have values in my webpage textboxes. Is there a way around this in my controller?
Update 1 - So I've changed my Create() code but now I dont understand ' and then change your code so that you do an Ajax POST, with your serialized form'
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult Create(BMICalculation CalculateBMI)
{
if (ModelState.IsValid)
{
CalculateBMI.Id = User.Identity.GetUserId();
CalculateBMI.Date = System.DateTime.Now;
CalculateBMI.BMICalc = CalculateBMI.CalculateMyBMI(CalculateBMI.Weight, CalculateBMI.Height);
CalculateBMI.BMIMeaning = CalculateBMI.BMIInfo(CalculateBMI.BMICalc);
db.BMICalculations.Add(CalculateBMI);
db.SaveChanges();
}
var data = new
{
BMICalculation = CalculateBMI.BMICalc,
BMIInfo = CalculateBMI.BMIMeaning
};
return Json(data, JsonRequestBehavior.AllowGet);
}
If I understood you right, when you are clicking the submit button, you are actually performing two requests, unaware of eachother:
One regular form post, that's processed, and returns with the view.
An Ajax GET request, that gets some data from ShowBMI on your controller. This Action requires a string and since you're not passing it any data, it returns null.
Again, if I understood you correctly, the scenario you want to achieve is that when you POST your form, you want a Json payload back with the processed result.
If that is correct, then what you'd probably want to do in have your Create action return the Json , and then change your code so that you do an Ajax POST, with your serialized form.Then in your Ajax success handler process the result you get back, just like you're doing now. I hope my intent is clear, otherwise, please ask for clarification. :)
You have to pass the required parameters to the method in controller...
You should try something like this..
function ShowBMI() {
$.ajax({
url: "#Url.Action("Create", "Your Controller")",
data: {
'CalculateBMI': your Data
},
dataType: 'json',
type: 'POST',
success: function (response) {
try {
alert(response.BMICalculation);
}
catch (error) {
}
},
error: function () {
}
});
}
Use With
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(BMICalculation CalculateBMI)
{
if (ModelState.IsValid)
{
return Json(obj, JsonRequestBehavior.AllowGet);
}
else
{
return View();
}
}
You can get the method using ActionResult and return it with Json Obejct also
I am writing a single page ajax app with ASP.NET MVC - making heavy use of jQuery. I do something similar to the following throughout the app:
JS:
$.ajax({
type: "GET",
url: "/Home/GetSomePartialView/",
data: someArguments,
success: function (viewHTML) {
$("#someDiv").html(viewHTML);
},
error: function (errorData) { onError(errorData); }
});
Controller C#:
public ActionResult GetSomePartialView(SomeArgumentModel someArguments)
{
return PartialView("_CaseManager");
}
This works great. The viewHTML (in the ajax success function) is returned as a string and I can shove it on the page no problem.
Now what I would like to do is to return not only the PartialView HTML string, but also some sort of status indicator. This is a permissions thing - for instance, if someone tries to get to a portion of they app they don't have permission to, I want to return a different PartialView than they asked for and also display a message in a popup window telling them why they got an View different from what they asked for.
So - to do this, I would like to do the following:
Controller C#:
public ActionResult GetSomePartialView(SomeArgumentModel someArguments)
{
ReturnArgs r = new ReturnArgs();
bool isAllowed = CheckPermissions();
if (isAllowed)
{
r.Status = 400; //good status ... proceed normally
r.View = PartialView("_CaseManager");
}
else
{
r.Status = 300; //not good ... display permissions pop up
r.View = PartialView("_DefaultView");
}
return Json(r);
}
public class ReturnArgs
{
public ReturnArgs()
{
}
public int Status { get; set; }
public PartialViewResult View { get; set; }
}
JS:
$.ajax({
type: "GET",
url: "/Home/GetSomePartialView/",
data: someArguments,
success: function (jsReturnArgs) {
if (jsReturnArgs.Status === 300) { //300 is an arbitrary value I just made up right now
showPopup("You do not have access to that.");
}
$("#someDiv").html(jsReturnArgs.View); //the HTML I returned from the controller
},
error: function (errorData) { onError(errorData); }
});
This SORTA works right now. I get a good object in JavaScript (what I am expecting to see), however I cannot see how to get at the full HTML string of the jsReturnArgs.View property.
I am really just looking for the same string that would be returned if I were just returning the PartialView by itself.
(As I mentioned at the beginning, this is a single page app - so I can't just redirect them to another View).
So - using the following posts I got this working:
Partial Views vs. Json (or both)
Render a view as a string
They both lay it out nicely, then I changed my code to the following:
C#:
public ActionResult GetSomePartialView(SomeArgumentModel someArguments)
{
ReturnArgs r = new ReturnArgs();
bool isAllowed = CheckPermissions();
if (isAllowed)
{
r.Status = 400; //good status ... proceed normally
r.ViewString = this.RenderViewToString("_CaseManager");
}
else
{
r.Status = 300; //not good ... display permissions pop up
r.ViewString = this.RenderViewToString("_DefaultView");
}
return Json(r);
}
public class ReturnArgs
{
public ReturnArgs()
{
}
public int Status { get; set; }
public string ViewString { get; set; }
}
JS:
$.ajax({
type: "GET",
url: "/Home/GetSomePartialView/",
data: someArguments,
success: function (jsReturnArgs) {
if (jsReturnArgs.Status === 300) { //300 is an arbitrary value I just made up right now
showPopup("You do not have access to that.");
}
$("#someDiv").html(jsReturnArgs.ViewString); //the HTML I returned from the controller
},
error: function (errorData) { onError(errorData); }
});
one way to skip having to return a json with multiple parameters and your html encoded as json is to send an HTML always but you send a hidden field that has the status set in it or something like that..
success: function(data)
{
if(data.find("#ajax-status").val()==="success")
{
$("#someDiv").html(data);
}
else
{
showPopup("You do not have access to that.");
}
}
I wouldnt recommend this appraoch I would have two partial views one for the normal view and the other for the error/unauthorized case..