I can't work out what I'm doing wrong - I'm sure this used to work...:
<script type="text/javascript">
$("##containerId form").submit(function (event) {
event.preventDefault();
var form = $(this);
if (form.valid()) {
$.post(form.attr('action'), form.serialize(), function(data) {
$("##containerId").replaceWith(data.result);
}, "json");
}
});
</script>
I have a function that returns a view result as a string so I can return it as an object within the JSON response:
protected string RenderViewResultToString(ViewResultBase viewResult) {
using (var sw = new StringWriter()) {
if (string.IsNullOrEmpty(viewResult.ViewName))
viewResult.ViewName = ControllerContext.RouteData.GetRequiredString("action");
ViewEngineResult result = null;
if (viewResult.View == null) {
result = viewResult.ViewEngineCollection.FindPartialView(ControllerContext, viewResult.ViewName);
if (result.View == null)
throw new InvalidOperationException("Unable to find view. Searched in: " + string.Join(",", result.SearchedLocations));
viewResult.View = result.View;
}
var view = viewResult.View;
var viewContext = new ViewContext(ControllerContext, view, viewResult.ViewData, viewResult.TempData, sw);
view.Render(viewContext, sw);
if (result != null)
result.ViewEngine.ReleaseView(ControllerContext, view);
return sw.ToString();
}
}
So, in my controller I have:
[HttpPost, ValidateInput(false)]
public JsonResult Edit(/* stuff */) {
bool success = true;
try {
/* stuff */
} catch {
/* stuff */
success = false;
}
return Json(new { success, result = RenderViewResultToString(/* stuff - call to something that gives a ViewResult */) });
}
In Chrome, I get: "Resource interpreted as Document but transferred with MIME type application/json." and it renders the JSON in the browser as text.
In Firefox/IE, it prompts me to download a file.
What gives?
The form submission isn't getting suppressed. The messages you are getting are from an actual form submission to a page that returns JSON. If you check the browser address bar, you should see the URL is different.
If you run $("##containerId form") in the console, you should see that you're getting no results. "#" is an invalid character in a selector and needs to be escaped. $("#\\#containerId form") should work.
Related
My Ajax method is calling action method in controller, and succeeding in the call return. However the object I am passing is always null.
I have read many, maybe not all as there are quite a few, similar questions. I have tried different things, such as different variations of removing dataType and contentType from the ajax function. I have set break points in the action and set alerts in scripts to verify the object is not null before sending to the JsonResult Action. I have verified that data from the Action method is reaching the succeeded section of the ajax function.
So Here is the scenario: I have an MVC Core 2.2 index page. I added a search textbox. everything works correctly If I block JS in the browser, So I know the HTML is correct. But I wanted to give an Ajax option for a "more pleasant" user experience. I actually did get the ajax to work on simple hard coded strings. But now for some reason the passed in object is null.
Lets start with the view's script:
//This is the Object I want passed through Ajax
//class pageValues {
// constructor(){
// this.sortColumn = $("#inpSortColumn").val();
// this.sortOrder = $("#inpSortOrder").val();
// this.filter = $("#Filter").val();
// this.message = "";
// this.currentPage = $("#inpCurrentPage").val();
// this.recordsPerPage = $("#inpPageSize").val();
// this.recordCount = 0;
// }
//}
// I also tried as a simple variable without a constructor and added
// default values incase undefined values were causing issues
var pageValues = {
sortColumn: ($("#inpSortColumn").val() == undefined ) ? "LastName" : $("#inpSortColumn").val(),
sortOrder: ($("#inpSortOrder").val() == undefined ) ? "ASC" : $("#inpSortOrder").val(),
filter: ($("#Filter").val() == undefined ) ? "" : $("#Filter").val(),
message: ($("#inpMessage").val() == undefined ) ? "" : $("#inpMessage").val(),
currentPage: ($("#inpCurrentPage").val() == undefined) ? 1: $("#inpCurrentPage").val(),
recordsPerPage: ($("#inpPageSize").val() == undefined) ? 5 : $("#inpPageSize").val(),
totalRecords: ($("#inpTotalRecords").val() == undefined ) ? 0 : $("#inpTotalRecords").val()
};
$(document).ready(function () {
// If we are here, the browser allows JS
// So, replace the submit buttons with Ajax functions
ReplaceHtml();
});
function ReplaceHtml() {
// Search Button
var divSearch = $("#divSearchBtn");
divSearch.hide();
divSearch.empty();
divSearch.append('<button id="btnAjaxSearch" type="button" ' +
'class="" onclick="RequestRecords();">Search</button>');
divSearch.show();
}
// Here we call the Ajax function passing the data object and the callback function
function RequestRecords() {
alert($("#Filter").val()); // This is just to Verify value is present
AjaxCallForRecords(pageValues, ReturnedData);
}
// This is the callback function
function ReturnedData(data) {
// This verifies we hit the callback
alert("inside ajax callback");
// The Verification that the Object returned is valid.
// The problem appeared here,
// The firstname was always the same no matter the Search Filter.
// Telling me the object on the server side receiving the 'pageValues'
// had been recreated due to being null.
alert(data.users[0].firstName);
}
// Of course, here is the ajax function
// I have played around with data and content settings
// When I changed those I got 'Response Errors' but could never get the ResponseText
function AjaxCallForRecords(dataToSend, callback) {
console.log(dataToSend); // This prove Data is here
$.ajax({
type: "GET",
url: '#Url.Action("Index_Ajax","ApplicationUsers")',
data: JSON.stringify(dataToSend),
dataType: "json",
contentType: "application/json",
success: function (data) { callback(data); },
error: function (data) { alert("Error. ResponseText: " + data.responseText); }
});
}
</script>
Ok, Now to the Controller:
public JsonResult Index_Ajax([FromBody] UsersCodeAndClasses.PageValues pageValues)
{
// A break point here reveals 'pageValues' is always null - this is the problem.....
// In the GetFilteredData function I do create a new 'pageValues' object if null
// So my Search 'Filter' will always be empty, and I will always get all the records.
// Get Records
List<InputUser> users = _usersCode.GetFilteredData(pageValues);
// The next block of code assembles the data to return to the view
// Again the 'pageValues' is null because that is what gets passed in, or rather, never assigned
//Build Return Data
UsersCodeAndClasses.AjaxReturnData data = new UsersCodeAndClasses.AjaxReturnData()
{
pageValues = pageValues,
users = users
};
return Json(data);
}
And Finally, The Server side 'pageValues' declaration:
public class PageValues
{
// Class used to pass page and sorting information to Ajax Call
public string sortColumn { get; set; } = "LastName";
public string sortOrder { get; set; } = "ASC";
public string filter { get; set; } = "";
public string message { get; set; } = "";
public int currentPage { get; set; } = 1;
public int recordsPerPage { get; set; } = 5;
public int recordCount { get; set; }
}
public class AjaxReturnData
{
// Class is used to pass multiple data to the Ajax Call
public PageValues pageValues { get; set; }
public List<InputUser> users { get; set; }
}
So, I am expecting data to be passed, I just do not know why the server is not assigning the data. I am new at this and could use an experienced eye.
Thanks
Simply change your type from GET to POST in Ajax call.
I spent some more time researching everything about ajax return values and classes.
Ultimately, my class was malformed, once I changed that it started working. I also changed the type to POST, I did not want to use POST just to read records. But I am sending a lot of data keeping up with search, pagination and sorting.
The below code works though I feel like it is very verbose and some parts may be unnecessary. Hope it helps someone, and please feel free to comment and help me out on things that could help others.
<script>
// Class to use for ajax data
class pageValues {
constructor(){
this.sortColumn = ($("#inpSortColumn").val() == undefined) ? "LastName" : $("#inpSortColumn").val();
this.sortOrder = ($("#inpSortOrder").val() == undefined) ? "ASC" : $("#inpSortOrder").val();
this.filter = ($("#Filter").val() == undefined) ? "" : $("#Filter").val();
this.message = ($("#inpMessage").val() == undefined) ? "" : $("#inpMessage").val();
this.currentPage = ($("#inpCurrentPage").val() == undefined) ? 1 : $("#inpCurrentPage").val();
this.recordsPerPage = ($("#inpPageSize").val() == undefined) ? 5 : $("#inpPageSize").val();
this.totalRecords= ($("#inpTotalRecords").val() == undefined) ? 0 : $("#inpTotalRecords").val();
}
get SortColumn() { return this.sortColumn; }
set SortColumn(value) { this.sortColumn = value; }
get SortOrder() { return this.sortOrder; }
set SortOrder(value) { this.sortOrder = value;}
get Filter() { return this.filter; }
set Filter(value) { this.filter = value; }
get Message() { return this.message; }
set Message(value) { this.message = value; }
get CurrentPage() { return this.currentPage; }
set CurrentPage(value) { this.currentPage = value; }
get RecordsPerPage() { return this.recordsPerPage; }
set RecordsPerPage(value) { this.recordsPerPage = value; }
get TotalRecords() { return this.totalRecords; }
set TotalRecords(value) { this.totalRecords = value; }
}
$(document).ready(function () {
// If we are here, the browser allows JS
// So, replace the submit buttons with Ajax functions
ReplaceHtml();
});
function ReplaceHtml() {
// Search Button
var divSearch = $("#divSearchBtn");
divSearch.hide();
divSearch.empty();
divSearch.append('<button id="btnAjaxSearch" type="button" ' +
'class="" onclick="RequestRecords();">Search</button>');
divSearch.show();
}
// Here we call the Ajax function passing the data object and the callback function
function RequestRecords() {
alert($("#Filter").val()); // This is just to Verify value is present
AjaxCallForRecords(new pageValues(), ReturnedData);
}
// This is the callback funtion
function ReturnedData(data) {
// The verification we hit the callback
alert("inside ajax callback");
alert(data.users[0].firstName);
}
// Ajax function
function AjaxCallForRecords(dataToSend, callback) {
console.log(dataToSend);
$.ajax({
type: "POST",
url: '#Url.Action("Index_Ajax","ApplicationUsers")',
data: JSON.stringify(dataToSend),
dataType: "json",
contentType: "application/json",
success: function (data) { callback(data); },
error: function (data) { alert("Error. ResponseText: " + data.responseText); }
});
}
</script>
I have a fairly large application with dozens of Ajax calls. I want to log any of the errors that come up in the ajax calls in one single place. I put a jquery alert on my _Layout.cshmtl view so that the exception can get passed into the alert. How do I return a error string from my HandleExceptionAttribute class to my view?
Controller Action:
[HandleExceptionAttribute]
[HttpPost]
[Authorize ( Roles = "View Only,Admin,A-Team Manager,A-Team Analyst" )]
public JsonResult GetEntitySorMapTable ( Decimal entityId )
{
//Added this line to hit my HandleExceptionAttribute
throw new DivideByZeroException();
List<EntitySorMapView> entitySorMaps = null;
if (entityId == 0)
{
entitySorMaps = new List<EntitySorMapView> ( );
}
entitySorMaps = RealmsModel.RealmsDataInterface ( ).SelectEntitySorMapByEntityId ( entityId );
String data = HtmlHelpers.BuildEntitySorMapTable ( entitySorMaps );
return new JsonResult ( )
{
Data = data,
MaxJsonLength = Int32.MaxValue
};
}
Error Attribute:
public class HandleExceptionAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception != null)
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
filterContext.Exception.Message,
filterContext.Exception.StackTrace
}
};
filterContext.ExceptionHandled = true;
}
else
{
base.OnException(filterContext);
}
}
}
_Layout View ajax error script:
<script type="text/javascript">
$(document).ajaxError(function (xhr, status, error) {
e.stopPropagation();
if (xhr.error != null)
alert('Error: ' + xhr.responseText + ' status: ' + status + ' Exception: ' + error);
});
</script>
You can use
filterContext.Result = new JsonResult
{
Data = new { errorMessage = "Your custom error message" },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
and in your _layout.cshtml file
$(document).ajaxError(function (event, jqxhr, settings, thrownError) {
if (jqxhr.error != null) {
var result = JSON.parse(jqxhr.responseText);
console.log(result.errorMessage)
}
});
what else do i need in my code please, I have this so far:
<script type="text/javascript">
function PostNewsComment(newsId) {
$.ajax({
url: "<%= Url.Action("AddCommentOnNews", "Home", new { area = "News" }) %>?newsId=" + newsId + "&newsComment=" + $("#textareaforreply").val(), success: function (data) {
$("#news-comment-content").html(data + $("#news-comment-content").html());
type: 'POST'
}
});
}
$("#textareaforreply").val("");
</script>
and
[HttpPost]
[NoCache]
public ActionResult AddCommentOnNews(int newsId, string newsComment)
{
if (!String.IsNullOrWhiteSpace(newsComment))
{
var currentUser = ZincService.GetUserForId(CurrentUser.UserId);
ZincService.NewsService.AddCommentOnNews(newsId, newsComment, currentUser.UserId);
Zinc.DataModels.News.NewsCommentsDataModel model = new DataModels.News.NewsCommentsDataModel();
var today = DateTime.UtcNow;
model.CommentDateAndTime = today;
model.NewsComment = newsComment;
model.Firstname = currentUser.Firstname;
model.Surname = currentUser.Surname;
model.UserId = CurrentUser.UserId;
return View("NewsComment", model);
}
return null;
}
<div class="actions-right">
<%: Html.Resource(Resources.Global.Button.Reply) %>
</div>
i have no idea how this works, because it is not working in FF???
and the other thing is i must not pass return null i must pass JSON false ???
any help please?
thanks
You should encode your request parameters. Right now you have concatenated them to the request with a strong concatenation which is a wrong approach. There's a property called data that allows you to pass parameters to an AJAX request and leave the proper url encoding to the framework:
function PostNewsComment(newsId) {
$.ajax({
url: '<%= Url.Action("AddCommentOnNews", "Home", new { area = "News" }) %>',
type: 'POST',
data: {
newsId: newsId,
newsComment: $('#textareaforreply').val()
},
success: function (data) {
$('#news-comment-content').html(data + $('#news-comment-content').html());
}
});
}
Also you haven't shown where and how you are calling this PostNewsComment function but if this happens on the click of a link or submit button make sure that you have canceled the default action by returning false, just like that:
$('#someLink').click(function() {
PostNewsComment('123');
return false;
});
and the other thing is i must not pass return null i must pass JSON false ???
You could have your controller action return a JsonResult in this case:
return Json(new { success = false });
and then inside your success callback you could test for this condition:
success: function (data) {
if (!data.success) {
// the server returned a Json result indicating a failure
alert('Oops something bad happened on the server');
} else {
// the server returned the view => we can go ahead and update our DOM
$('#news-comment-content').html(data + $('#news-comment-content').html());
}
}
Another thing you should probably be aware of is the presence of dangerous characters such as < or > in the comment text. To allow those characters I would recommend you build a view model and decorate the corresponding property with the [AllowHtml] attribute:
public class NewsViewModel
{
public int NewsId { get; set; }
[AllowHtml]
[Required]
public string NewsComment { get; set; }
}
Now your controller action will obviously take the view model as argument:
[HttpPost]
[NoCache]
public ActionResult AddCommentOnNews(NewsViewModel viewModel)
{
if (!ModelState.IsValid)
{
var currentUser = ZincService.GetUserForId(CurrentUser.UserId);
ZincService.NewsService.AddCommentOnNews(viewModel.NewsId, viewModel.NewsComment, currentUser.UserId);
var model = new DataModels.News.NewsCommentsDataModel();
var today = DateTime.UtcNow;
model.CommentDateAndTime = today;
model.NewsComment = newsComment;
model.Firstname = currentUser.Firstname;
model.Surname = currentUser.Surname;
model.UserId = CurrentUser.UserId;
return View("NewsComment", model);
}
return Json(new { success = false });
}
I've seen the myriad threads sprawled across the Internet about the following similar code in an AJAX request returning undefined:
AJAX.onreadystatechange = function() {
if(AJAX.readyState == 4) {
if(AJAX.status == 200) {
var response = AJAX.responseText;
return response;
}
else {
window.alert('Error: ' + AJAX.status);
return false;
}
}
};
I know that I'm supposed to "do something with" responseText like writing it to the HTML. The problem: I don't have that luxury. This bit of code is intended to be inside of a generic method for running fast AJAX requests that way all the code for making an AJAX request doesn't have to written out over and over again (~40×) with the chance of a minor problem here or there that breaks the application.
My method HAS to explicitly return responseText "or else." No writing to HTML. How would I do this? Also, I'd appreciate a lack of plugs for JQuery.
What I'm looking for:
function doAjax(param) {
// set up XmlHttpRequest
AJAX.onreadystatechange = function() {
if(AJAX.readyState == 4) {
if(AJAX.status == 200) {
var response = AJAX.responseText;
return response;
}
else {
window.alert('Error: ' + AJAX.status);
return false;
}
}
};
// send data
}
...
function doSpecificAjax() {
var param = array();
var result = doAjax(param);
// manipulate result
}
Doing a little research I came across this SOF post:
Ajax responseText comes back as undefined
Based on that post, it looks like you may want to implement your ajax method like this:
function doRequest(url, callback) {
var xmlhttp = ....; // create a new request here
xmlhttp.open("GET", url, true); // for async
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState==4) {
if (xmlhttp.status == 200) {
// pass the response to the callback function
callback(null, xmlhttp.responseText);
} else {
// pass the error to the callback function
callback(xmlhttp.statusText);
}
}
}
xmlhttp.send(null);
}
Then, you can call that method like this...
doRequest('http://mysite.com/foo', function(err, response) { // pass an anonymous function
if (err) {
return "";
} else {
return response;
}
});
This should return the responseText accurately. Let me know if this doesn't give you back the correct results.
I am notable to naviagate to another page using Redirect ie when result is false, then i would like to navigate to exception page which is not happening.
public ActionResult IsLoginExsit(CustomerDO loginData)
{
if (!string.IsNullOrEmpty(loginData.UserName) && !string.IsNullOrEmpty(loginData.Password))
{
bool result = Businesss.Factory.BusinessFactory.GetRegistrations().IsLoginExist(loginData.UserName, loginData.Password);
if (result)
{
CustomerDO custInfo = new CustomerDO();
JsonResult jsonResult = new JsonResult();
jsonResult.Data = loginData;
custInfo = Businesss.Factory.BusinessFactory.GetRegistrations().GetCustInfoByUserName(loginData.UserName);
SessionWrapper.SetInSession("CustomerID", custInfo.Id);
SessionWrapper.SetInSession("CustomerFirstName", custInfo.FirstName);
SessionWrapper.SetInSession("CustomerLastName", custInfo.LastName);
return jsonResult;
}
else
{
return RedirectToAction("UnAuthorized", "Exceptions");
}
}
return View();
}
You seem to be invoking this action using AJAX. If you want to redirect this should be done on the client side in the success callback of this AJAX call using window.location.href. So for example you could adapt your action so that in case of error it returns a JSON object containing the url to redirect to:
else
{
return Json(new { errorUrl = Url.Action("UnAuthorized", "Exceptions") });
}
and then inside your AJAX success callback:
success: function(result) {
if (result.errorUrl) {
window.location.href = result.errorUrl;
} else {
...
}
}