MVC JSON ActionResult returns all page HTML output
When MVC widgets are added to templates in sitefinity, controller calls posted to ActionResults from jQuery or in my case Kendo MVC components return the JsonResult followed by the entire View output.
For example I have a simple Kendo Grid wired up to CRUD JSON ActionResult methods in the controller. The grid and actions work as expected in a standard MVC project...
Here is the Read Method as an example:
[HttpPost]
public
ActionResult GetCompanies([DataSourceRequest]DataSourceRequest request)
var entities = (from c
in
db.Companies
select c).ToList();
DataSourceResult result = entities.ToDataSourceResult(request);
return
Json(result);
In a standard MVC Project when viewing the results in Firebug are:
"Data"
:[
"CompanyId"
:1,
"CompanyName"
:
"123Company"
,
"Locations"
:[],
"CompanyId"
:6,
"CompanyName"
:
"678 Company"
,
"Locations"
:[],
"CompanyId"
:10,
"CompanyName"
:
"456 Company"
,
"Locations"
:[]],
"Total"
:3,
"AggregateResults"
:
null
,
"Errors"
:
null
Here are the results using the exact same code in Sitefinity (truncated the output to save space as the output is the entire index output):
"Data"
:[
"CompanyId"
:1,
"CompanyName"
:
"123Company"
,
"Locations"
:[],
"CompanyId"
:6,
"CompanyName"
:
"678 Company"
,
"Locations"
:[],
"CompanyId"
:10,
"CompanyName"
:
"456 Company"
,
"Locations"
:[]],
"Total"
:3,
"AggregateResults"
:
null
,
"Errors"
:
null
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
<html xmlns=
"http://www.w3.org/1999/xhtml"
>
<head><meta http-equiv=
"content-type"
content=
"text/html; charset=utf-8"
/><meta name=
"Generator"
content=
"Sitefinity 5.1.3450.0 CE"
/><link href=
"/SFRepWeb/Telerik.Web.UI.WebResource.axd?compress=0&_TSM_CombinedScripts_=%3b%3bTelerik.Sitefinity.Resources%2c+Version%3d5.1.3450.0%2c+Culture%3dneutral%2c+PublicKeyToken%3dnull%3aen%3aa14cc7f5-9d82-48ad-8683-c9402ebc4a01%3a7a90d6a%3ad271177c%3adff30785"
type=
"text/css"
rel=
"stylesheet"
/><title>
test123
</title></head>
<body>
<form method=
"post"
action=
""
id=
"aspnetForm"
>
<div class=
"aspNetHidden"
>
<input type=
"hidden"
name=
"ctl12_TSM"
id=
"ctl12_TSM"
value=
""
/>
<input type=
"hidden"
name=
"ctl13_TSSM"
id=
"ctl13_TSSM"
value=
""
/>
<input type=
"hidden"
name=
"__EVENTTARGET"
id=
"__EVENTTARGET"
value=
""
/>
------- OUTPUT TRUNCATED ---------------------------------------
You can see that it is basically returning the JSON result first followed by the controllers index cshtml output. This is causing Kendo Components to either not render or parse when all they expect is JSON, return null parameters on CRUD method executions, etc.
I assume this is the result of some interferance with MVC operations in order to support MVC within Sitefinity which by the way I think is great.
If so is there any kind of Attribute that can be put on methods to tellSitefinity not to output the page and only the returned results?
Is there any way to prevent this from happening, or also am I doing something wrong that I am not aware of?
Found another article that suggested from the documentation that
explained how to run a controller in classic mode. At first I thought
this would be an issue, but as it turns out it was a good suggestion and
works great to prevent full page output where we only want JSON.
Basically I just created a Global.asax in my sitefinity project. And put the following in the Application_Start method...
protected
void
Application_Start(
object
sender, EventArgs e)
Bootstrapper.MVC.MapRoute(
"Classic"
,
"JSON/controller/action"
,
new
action =
"Index"
);
@(Html.Kendo().Grid<
Company
>()
.Name("Companies")
.ToolBar(tb =>
tb.Create();
tb.Save();
)
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Pageable()
.Navigatable()
.DataSource(dataSource => dataSource.Ajax()
.Batch(true)
.ServerOperation(false)
.Model(model => model.Id(p => p.CompanyId))
.Read(create => create.Url("./JSON/Company/GetCompanies"))
.Update(update => update.Url("./JSON/Company/UpdateCompany"))
.Create(insert => insert.Url("./JSON/Company/InsertCompany"))
.Destroy(destroy => destroy.Url("./JSON/Company/DeleteCompany"))
)
.Columns(cols =>
cols.Bound(c => c.CompanyName);
cols.Command(cmd =>
cmd.Edit();
// cmd.Destroy();
);
)
)
I font another way to fix this , you just need to
1.Add a boolean with default value false in the controller.
2.Set the value to true in every method which returns json.
3.Override the OnResultExecuted method and call Response.End() if the boolean is true.
you get only json in the response.
Thanks Mihail. Telerik told me they are working on a fix for this.
Mihaul's suggestion worked great for me. I posted some code to help:
In my controller with the JsonResponse:
protected override void OnResultExecuted(ResultExecutedContext ctx)
// if we have a JSON result to return do not allow OnResultExecuted to execute or it will return unwanted results (HTML markup)
if (isJsonReturn) Response.End();
base.OnResultExecuted(ctx);
My Controller function returning Json:
[HttpPost]
public JsonResult BuildJsonString(MenuModel model)
// setting isJsonReturn to true will enable this method to return JSON and disable all sitefinity functionality from here forward
isJsonReturn = true;
MenuModel menu = new MenuModel();
menu.Item = "item1";
return Json(menu);
jQuery in the View:
$.ajax(
type: 'POST',
url: '/Menu/BuildJsonString',
cache: false,
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify( Item: "item1" ),
success: function (response)
console.log(JSON.stringify(response));
,
error: function (response)
console.log(JSON.stringify(response));
);
Hi,
Thank you for sharing your solutions and workarounds with the community in this forum thread. We got a couple of reports of this improper behavior and we have identified it to be a bug. It is logged for fixing. We will do our best to fix the bug as soon as possible.
Regards,
Stefani Tacheva
the Telerik team
Thanks, all i need:
protected override void OnResultExecuted(ResultExecutedContext ctx)
if (Request.IsAjaxRequest())
Response.End();
base.OnResultExecuted(ctx);
Hello Trung,
Thank you for sharing your source code and solution here.
Regards,
Stefani Tacheva
the Telerik team
:( i still get the whole Page returning if i use the Content method, and only errors if i use the JSON one , I'm assuming the error is due to invalid JSON? (the widget is on a page called hyweltest)
.net
public class TestController : Controller
public int Number get; set;
public string Name get; set;
protected override void OnResultExecuted(ResultExecutedContext ctx)
if (Request.IsAjaxRequest())
Response.End();
base.OnResultExecuted(ctx);
[HttpPost]
public ActionResult ContentTest(TestModel model)
return Content(<string containing valid JSON>);
[HttpPost]
public JsonResult AJAXTest(TestModel model)
return Json(model,JsonRequestBehavior.AllowGet);
javascript
var actionURL = pageURI + '/AJAXTest',
actionURL = '/hyweltest/AJAXTest';
$.ajax(
type: 'POST',
url: actionURL,
cache: false,
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(mvvmWrapper.model),
success: function (response)
alert("Success");
,
error: function (response)
alert("Fail");
);
Any help on this would be most apprecieated
FYI, the result i get back in my error response is the following - it looks like the rest of the page is "wrapping" the json.
"readyState":4,"responseText":"\r\n\r\n\r\n
<meta name='\"Generator\"' content='\"Sitefinity' version\"="" trial="" pu="" 6.1.4600.0="">
<link href='\"/Telerik.Web.UI.WebResource.axd?compress=0&_TSM_CombinedScripts_=%3b%3bTelerik.Sitefinity.Resources%2c+Version%3d6.1.4600.0%2c+Culture%3dneutral%2c+PublicKeyT3\"' rel='\"stylesheet\"' type='\"text/css\"'>
<link href='\"/Sitefinity/WebsiteTemplates/MyTheme1/App_Themes/MyTheme1/global/Reset.css?v=634798559040000000\"' rel='\"stylesheet\"' type='\"text/css\"'>
<link href='\"/Sitefinity/WebsiteTemplates/MyTheme1/App_Themes/MyTheme1/global/Layout.css?v=635150288835325922\"' rel='\"stylesheet\"' type='\"text/css\"'>
<title>\r\n\tHywelTest\r\n</title>\r\n\r\n
<form id='\"aspnetForm\"' action='\"AJAXTest\"' method='\"post\"'>
<div class='\"aspNetHidden\"'>
<input name='\"ctl08_TSM\"' id='\"ctl08_TSM\"' type='\"hidden\"' value='\"\"'>
<input name='\"ctl09_TSSM\"' id='\"ctl09_TSSM\"' type='\"hidden\"' value='\"\"'>
<input name='\"__EVENTTARGET\"' id='\"__EVENTTARGET\"' type='\"hidden\"' value='\"\"'>
<input name='\"__EVENTARGUMENT\"' id='\"__EVENTARGUMENT\"' type='\"hidden\"' value='\"\"'>
<input name='\"__VIEWSTATE\"' id='\"__VIEWSTATE\"' type='\"hidden\"' value='\"/wEPDwUKMTg3ODYyNzE1N2QYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgEFH2N0bDExJGN0bDAwJGN0bDAwJHdpbmRvd01hbmFnZXJpoXZ24sm/kczFSXtvy19fbwE5VuMeRqImXPTviRKSAw==\"'>
<input name='\"ctl08\"' id='\"ctl08\"' type='\"hidden\"'>
<div class='\"sfPublicWrapper\"' id='\"PublicWrapper\"'>
<div class='\"sf_cols\"'>
<div class='\"sf_colsOut' sf_2cols_1_75\"="">
<div class='\"sf_colsIn' id='\"T27B2843A001_Col00\"' sf_2cols_1in_75\"="">
\"Number\":234234234,\"Name\":\"fsgdfgsdfgsgf\",\"JSONString\":\"\\\"Name\\\":\\\"fsgdfgsdfgsgf\\\",\\\"Number\\\":234234234\"","status":200,"statusText":"OK"
</div></div></div></div></form>
The above method did not work for me, so A Horrible work around.......
[HttpPost]
public JsonResult AJAXTest(TestModel model)
Response.Clear();
Response.Write(<JSON STRING>);
Response.End();
return null;
Hope this helps others to avoid the pain i have been feeling for over 48 hours....... Sitefinity team, please can you fix the underlying bug, so i can make my code pretty again!!!!!
Hywel, what version of Sitefinity are you using? I wonder if the previously mentioned fixes have stopped working in SF6.1.
I am on 6.1
OK, that explains it then. For some reason the fix has stopped working in 6.1. Any ideas Stefani?
Hello,
The other workaround I can offer is to use classic MVC mode and register a custom route. You can directly route to your controller, and take Sitefinity out of the picture.
Here is how you can register a route with a custom prefix (in your Global.asax Application_Start method):
Bootstrapper.MVC.MapRoute(
"Json"
,
"json/controller/action/id"
,
new
controller =
"JsonController"
, action =
"Json"
, id = (
string
)
null
);
Thanks but I need it to work in Hybrid mode!
So it looks like SF6.1 puts the html first and then the JSON which stops the fix working. I've come up with this alternative fix:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
if (Request.IsAjaxRequest())
Response.Clear();
base.OnActionExecuting(filterContext);
protected override void OnResultExecuted(ResultExecutedContext filterContext)
if (Request.IsAjaxRequest())
Response.End();
base.OnResultExecuted(filterContext);
Hi, I am still very new to Sitefinity,
Can anyone please let me know if they are confident that it properly and robustly supports MVC?
Everything I have tried in so far seems like a bit of a hack!
If you can avoid it, it might be best. I can't find answers to the problems I've encountered.
This doesn't work at all for me unfortunately.
Isn't there something we can do to make this work? This thread is a year old and nothing in here seems to work.
Hey Chris, could you post your code for the controller?
I fixed this. The issue was related to OpenAccess and json serialization. The OnResultExecuted would never fire and the action would hang indefinitely.
To Hywel. We have been fighting with Sitefinity to make MVC work and its a mess. They are on the right track but not there yet.