I have the following Action in my controller:
[HttpPost]
public JsonResult RedirectToAspReportViewer(MvcReportPeriodSelectionViewModel periodFilter, MvcMultipleLocationSelectionViewModel locationFilter)
{
var jsonObject = new { HasErrors = false, strUrl = "/ASPReports/TreatmentOutcome.aspx" };
if (ModelState.IsValid)
{
try
{
//some code
}
catch (ValidationException ex)
{
this.HandleValidationErrors(ex);
jsonObject = new { HasErrors = true, strUrl = "/TreatmentOutcomeReport/Report/" };
}
}
return Json(jsonObject);
}
Then, in my Javascript, I have the following function, which get's called on my ajax post's OnSuccess function.
onSuccessCall: function (response) {
if (response.HasErrors === true) {
//window.location = window.location.href;
location.reload();
} else {
window.open(response.strUrl);
}
};
As you can see from above, if my reponse object has errors, I would like to stay on the current page, just refresh it so that my ModelState errors would still show.
The problem I am facing, is that when I call location.reload, my ModelState errors do not show on my page. I have a feeling it is because I am posting to the server again, and the ModelState gets cleared.
How can I avoid this?
UPDATE
I cannot just add the validation error to the JsonResult, and in the client side, update the necessary DOM to display the errors. On all of my views, I have the following shared view which returns my errors: Below is the sample:
#model ModelStateDictionary
#{
var cls = "alert-danger";
var type = ViewBag.Type;
if (type != null && type.ToString().ToLower() == "warning")
{
cls = "alert-warning";
}
var message = "Please attend to the following problems:";
if (ViewBag.Message != null && ViewBag.Message.ToString().Trim() != "")
{
message = ViewBag.Message.ToString().Trim();
}
}
#if (ViewData.ModelState.Keys.Any(k => ViewData.ModelState[k].Errors.Count() > 0))
{
<div class="alert #cls">
<button class="close" data-dismiss="alert" aria-hidden="true">× </button>
#Html.ValidationSummary(false, message)
</div>
}
This will get called at the top of all my views as follow:
<div id="valSummary">
#Html.Partial("_ValidationSummaryDisplay", ViewData.ModelState)
</div>
If you want your ModelState errors to show on the page, then you should
return View(yourViewModel);
When coding the view, be sure to include the helpers to show your validation:
#Html.ValidationMessage(m => m.PropertyName)
I am assuming your handle method puts the errors in the ModelState (as that is what it is for).
Related
I'm creating an ASP.NET MVC application which uses "SqlDependecy" and "SignalR" technologies to maintain real-time communication with the server based on database changes. It simply inspect a field value changes in specific database record and then display it on the browser.
The attempt works perfectly fine. But when I monitor the network requests through the browsers "Network" performance, the request count increases by 1 in every refresh of the page.
As in the image.
Initial page load only make one request.
First refresh after the initial load and then db change will lead to make 2 requests.
Second refresh after the initial load and then db change will lead to make 3 requests.
so on...
The js code I tried is given below.
It seams as an problem to me. If this is a real problem, Any advice on this will be highly appreciated. Thank you very much.
<script type="text/javascript">
$(function () {
var jHub = $.connection.journeyHub;
$.connection.hub.start();
jHub.client.ListenChange = function () {
getData();
}
jHub.client.ListenChange();
});
function getData() {
$.ajax({
url: 'GetValue',
type: 'GET',
dataType: 'json',
success: function (data) {
if (data == "pending") {
$("#box").css({ "background-color": "orange" });
}
else if (data == "deny") {
$("#box").css({ "background-color": "red" });
}
else if (data == "success") {
$("#box").css({ "background-color": "green" });
}
}
});
}
</script>
<div id="box" style="width:100px; height:100px; background-color: gray;"></div>
[Edit v1]
Here is my Controller where the event handler is located.
public class TravelController : Controller
{
SqlConnection link = new SqlConnection(ConfigurationManager.ConnectionStrings["linkTraveller"].ConnectionString);
// GET: Travel
public ActionResult Listen()
{
return View();
}
public ActionResult GetValue()
{
using (IDbConnection conn = link)
{
string query = #"SELECT [Status] FROM [dbo].[Journey] WHERE [Id]=1";
SqlCommand command = new SqlCommand(query, link);
SqlDependency sqlDep = new SqlDependency(command);
sqlDep.OnChange += new OnChangeEventHandler((sender, e) => sqlDep_OnChange(sender, e));
conn.Open();
string status = command.ExecuteScalar().ToString();
return Json(status, JsonRequestBehavior.AllowGet);
}
}
private void sqlDep_OnChange(object sender, SqlNotificationEventArgs e)
{
JourneyHub.Start();
}
}
Here is the Hub
public class JourneyHub : Hub
{
public static void Start()
{
var context = GlobalHost.ConnectionManager.GetHubContext<JourneyHub>();
context.Clients.All.ListenChange();
}
}
Off the top of my head, I would say you are not decrementing your trigger handlers, sql dependency triggers only fire once and then they are gone, you have to remember the remove the event handler for it or they just keep adding but, but I will know for sure if you can post your sql dependency trigger code.
Here is a sample from something I did many years ago, but the idea is still the same.
try
{
using (
var connection =
new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT [Id]
,[FName]
,[LName]
,[DOB]
,[Notes]
,[PendingReview]
FROM [dbo].[Users]",
connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
command.ExecuteReader();
}
}
}
catch (Exception e)
{
throw;
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = sender as SqlDependency;
if (dependency != null) dependency.OnChange -= dependency_OnChange;
//Recall your SQLDependency setup method here.
SetupDependency();
JobHub.Show();
}
On the server-side I have a transaction which returns a JsonResult:
public JsonResult DoStuff(Guid id, string userInputText)
{
var product = _repository.Product(id); //busines logic
//Only a specific product must have userInputText <= 10 characters.
//Other products may have as many characters as the user wants.
if(product == Enum.SpecificProduct && userInputText.Count() > 10)
{
//The user input text comes from the View...
//If it has more then 10 characters, need to send the errorMessage to the View.
return Json(new { success = false, errorMessage = "error message" }, JsonRequestBehavior.AllowGet);
}
//Otherwise, do stuff on the product...
//and return success at the end.
return Json(new { success = true });
}
On the other hand, on the client-side I have this:
using (Ajax.BeginForm("DoStuff", ajaxOptions))
{
<span>Enter the text:</span>
#Html.TextArea("userInputText", new { onkeyup = "SyncContents(); return false;" })
<input type="submit" value="Add" />
<!-- error message should be displayed here-->
}
This is the AjaxOptions:
var ajaxOptions= new AjaxOptions
{
OnSuccess = "reload",
OnFailure = "FailMessage"
};
If the entered text have more then 10 characters, when the "Add" button is pressed, the Controller is being executing the code on the server-side and fails, how can I get the errorMessage from there and use it here, in the View, to inform the user ?
I tried to alert a message:
<script>
function FailMessage() {
alert("Fail Post");
}
</script>
But no pop-up "Fail post" appears.
Best regards.
The problem here is the Ajax helper thinks all your responses are successful. Your controller action is returning HTTP 200 so there isn't a problem.
https://msdn.microsoft.com/en-us/library/system.web.mvc.ajax.ajaxoptions.onfailure(v=vs.118).aspx#P:System.Web.Mvc.Ajax.AjaxOptions.OnFailure
AjaxOptions.OnFailure Property
This function is called if the response status is not in the 200 range.
So you'll need to use the success handler and explicitly check the JSON success parameter.
Or have your action change the HttpStatusCode for the response.
if (notValid)
{
Response.StatusCode = 400; // Bad Request
return Json(new { success = false, errorMessage = "error message" }, JsonRequestBehavior.AllowGet);
}
But for a validation error here I'd just check the for an error in the success handler.
And yes, you should validate on the client and on the server.
I seem to have a problem sending an alert (toast) message from a controller action using SignalR. Unless I add a Thread.Sleep() after the send call, I never see the message. Inevitably, the send call occurs before return View(), so I imagine the alert is visible for a millisecond on the previous view, until the new one is served.
My first, rough solution is to use a timer to keep sending the message, until I get an acknowledgement.
This solution stinks. What else can I do? I can have the 'receiving' pages poll the server to see if they have any alerts, but that defeats the purpose of SignalR.
But then, maybe SignalR isn't suited to my case and I should just send the alerts as json strings on the model.
From the Login action:
....
//ModelState.AddModelError("", "Invalid login attempt.");
AlertsHub.ShowClientAlert(new Alert(AlertLevel.Error, "Invalid login attempt."));
return View(model);
The hub:
public class AlertsHub : Hub
{
private static Alert pendingAlert;
static Timer pollTimer;
internal static void ShowClientAlert(Alert alert)
{
if (pendingAlert != null)
{
return;
}
pendingAlert = alert;
pollTimer = new Timer(_ => SendAlert(pendingAlert), null, 0, 500);
}
static private void SendAlert(Alert alert)
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<AlertsHub>();
context.Clients.All.ShowAlert(alert.Level.ToString(), alert.Message, alert.Title);
}
[HubMethodName("AlertReceived")]
public void AlertReceived(Guid alertId)
{
pollTimer.Dispose();
pendingAlert = null;
}
}
From the JS:
var toast;
$(function () {
if (typeof toast != 'undefined' && toast != null) {
showToast(toast);
toast = null;
}
var alertsProxy = $.connection.alertsHub;
alertsProxy.client.showAlert = function(alertId, level, message, title) {
toast.alertId = alertId;
toast.level = level;
toast.message = message;
toast.title = title;
};
$.connection.hub.start()
.done(function () {
console.log('Now connected, connection ID=' + $.connection.hub.id);
})
.fail(function () {
console.log('Could not Connect!');
});
});
function showToast(toast) {
switch (toast.level.toLowerCase()) {
case "success":
toastr.success(toast.message, toast.title);
break;
...
}
alertsProxy.server.AlertReceived(alertId)
.done(function() {
console.log("Alert '" + alertId + "' acknowledged.");
})
.fail(function() {
console.log("Acknowledgement of alert '" + alertId + "' failed.");
});
}
The javascript is supposed to handle form submission. However, even if called with
script src="js/registerform.js"> Uncaught ReferenceError: sendreg is not defined .
The function is called onclick. Can be reproduced on www.r4ge.ro while trying to register as well as live edited. Tried jshint.com but no clue.
I will edit with any snips required.
function sendreg() {
var nameie = $("#fname").val();
var passwordie = $("#fpass").val();
var emailie = $("#fmail").val();
if (nameie == '' || passwordie == '' || emailie == '') {
alert("Please fill all the forms before submitting!");
} else {
// Returns successful data submission message when the entered information is stored in database.
$.post("http://r4ge.ro/php/register.php", {
numeleluii: nameie,
pass: passwordie,
mail: emailie
}, function(data) {
alert(data);
$('#form')[0].reset(); // To reset form fields
setTimeout(fillhome, 1000);
});
}
}
function sendpass() {
var oldpassw = $("#oldpass").val();
var newpassw = $("#newpass").val();
if (oldpassw == '' || newpassw == '') {
alert("Please fill all the forms before submitting!");
} else {
// Returns successful data submission message when the entered information is stored in database.
$.post("http://r4ge.ro/php/security.php", {
xoldpass: oldpassw,
xnewpass: newpassw
}, function(data2) {
alert(data2);
$('#passform')[0].reset(); // To reset form fields
});
}
}
function sendmail()
{
var curpass = $("#curpass").val();
var newmail = $("#newmail").val();
if (curpass == '' || newmail == '')
{
alert("Please fill all the forms before submitting!");
}
else
{
// Returns successful data submission message when the entered information is stored in database.
$.post("http://r4ge.ro/php/security.php", {
curpass: curpass,
newmail: newmail
}, function(data3) {
alert(data3);
$('#mailform')[0].reset(); // To reset form fields
});
}
}
I'm guessing here but... I imagine you are doing something like
...<button onclick="sendreg">...
And you have your <script> in the bottom on the code. Just put them on top or use $("#mybtn").click(sendreg)
Try using $("#mybtn").click(sendreg) instead of inline onclick.
The script wasn't called in the html. sorry for wasting time. A simple
<script src="js/registerform.js"></script> Fixed it.
There is no syntax error there, and I don't see any such error when trying the page.
The error that you get is that you can't make a cross domain call. Do the request to the same domain:
$.post("http://www.r4ge.ro/php/register.php", {
or:
$.post("/php/register.php", {
I'm trying to write a straightforward comment poster. I have this code in the controller:
[HttpPost]
[ValidateInput(false)]
public ViewResult Comments(MemberData md, long EntryId, string Comment, long LastId = 0)
{
bool isModerated = true;
bool isLoggedIn = GenesisRepository.IsNotGuest(md.MemberGUID);
bool isCommentAllowed = GenesisRepository.IsPermissionAssigned(md.MemberGUID, "Comments", "Create");
// Moderate comment?
if (moderateGuestComments == false && isLoggedIn == false) isModerated = false;
if (moderateMemberComments == false && isLoggedIn) isModerated = false;
long memberId = (from m in GenesisRepository.Member
where m.MemberGUID == md.MemberGUID
select m.MemberID)
.FirstOrDefault();
if (
EntryId > 0
&& !string.IsNullOrEmpty(Comment)
&& memberId > 0
&& isCommentAllowed)
{
Comments comment = new Comments {
Comment = Comment,
Date = DateTime.Now,
isActive = isModerated ? false : true,
MemberID = memberId,
StreamEntryID = EntryId,
};
if (GenesisRepository.SaveComment(comment))
{
List<Comments> comments = new List<Comments>();
comments = (from c in GenesisRepository.Comments
where c.StreamEntryID == EntryId
&& c.comID > LastId
select c
).ToList();
return View("DisplayComments", comments);
}
}
return View("CommentError", "Unable to post comment.");
}
When everything is fine and the action returns return View("DisplayComments", comments); the $.post() success function is triggered. But, When the action returns return View("CommentError", "Unable to post comment."); The $.post() ajax fails. I don't understand why the $.post() cares which view I'm returning.
Here's my Javascript:
<script type="text/javascript">
$(document).ready(function () {
$("#comments").ajaxError(function (event, request, settings) {
alert("Error requesting page " + settings.url);
});
$("button#submitComment").click(function () {
var commentList = $("#comments");
var lastId = $(".comment h4").last().attr("id");
var commentData = "EntryId=" + $("input#EntryID").val()
+ "&Comment=" + $("textarea#Comment").val()
+ "&LastId=" + lastId;
$.post(
"/find/Comments/Comments",
commentData,
function (data) {
alert("success");
alert(data);
if ($(data).filter(".error").length > 0) {
error = $(data);
$(this).after(error);
}
else {
newComments = $(data);
newComments.filter(".comment").css('display', 'none');
alert(newComments);
commentList.append(newComments);
$(".comment").each(function () {
$(this).slideDown("fast")
});
$("#Comment").attr("value", "");
}
}
);
});
});
</script>
What about this could cause the ajax to fail?
Here's what the two views look like:
View("DisplayComments", comments); (works)
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<Genesis.Domain.Entities.Comments>>" %>
<% foreach (var item in Model) %>
<% { %>
<div class="comment" style="background:#eee; border:1px solid gray; padding:10px 10px 0 10px; margin-bottom:20px;">
<h4 id="<%:item.comID %>"><%: item.Member.ScreenName%> commented on <%: String.Format("{0:f}", item.Date)%></h4>
<p>
<%: item.Comment%>
</p>
</div>
<% } %>
View("CommentError", "Unable to post comment."); (does not work)
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>
<div class="error">
<%:Model%>
</div>
What about this could cause the ajax post to fail?
If the ajaxError function is triggered this strongly indicates that your controller action returns a status code different than 200, probably 500 which is a strong indication that your controller action throws an exception before ever reaching the last line and be able to return a view.
So here are the steps to do:
Use FireBug
Look at what your server sends as response to the AJAX request
Analyze the response status code and the response contents
Alternative approach:
Put a breakpoint in your controller action
Hit F5
When the controller action is hit step through your code
Observe exactly what happens
Remark: I would very strongly recommend you properly encoding your AJAX input. So instead of:
var commentData = "EntryId=" + $("input#EntryID").val()
+ "&Comment=" + $("textarea#Comment").val()
+ "&LastId=" + lastId;
you definitely should:
var commentData = $.param({
EntryId: $("input#EntryID").val(),
Comment: $("textarea#Comment").val(),
LastId: lastId
});
Note that everytime you use the +, & and = signs when dealing with querystring parameters (no matter what language you are using) you are doing it wrong.