TempData not displaying success message Asp.net MVC - asp.net-mvc

I'm using tempdata to give user a "success message" before redirection however at the moment it just redirect without giving the message. Maybe I have it at wrong place, I tried moving it to different parts of the code but it still didn't work.
Index View
#{
ViewBag.Title = "Home Page";
}
#if (TempData["notice"] != null)
{
<p>#TempData["notice"]</p>
}
<div>
<img src="~/Content/Images/TWO_Logo.jpg" alt="Logo" />
</div>
<div class="jumbotron">
<h1></h1>
<p class="lead">
</div>
Home Controller
namespace com.twcl.it.isms.Controllers
{
[Authorize(Roles = "Administrator, Issue, Transaction_Edit, Print")]
public class HomeController : Controller
{
public ActionResult Index()
{
TempData["notice"] = "Your item(s) successfully requested";
ViewBag.SuccessMessage = TempData["SuccesMeassge"];
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
public ActionResult Logoff()
{
HttpContext.Session.Abandon();
Response.Redirect("http://192.168.5.127");
//Response.StatusCode = 401;
//Response.End();
//////throw new HttpException(401, "Please close your browser to complete Log Off");
return View("Index");
}

In Index Action method just add TempData.Keep("notice"); which will keep tempdata available for next hop.
public ActionResult Index()
{
TempData.Keep("notice");
ViewBag.SuccessMessage = TempData["SuccesMeassge"];
return View();
}
On View to display alert message
#if (TempData.ContainsKey("notice"))
{
<script type="text/javascript">
alert(#TempData["notice"]);
</script>
}
Details about keep and peek.
when-to-use-keep-vs-peek-in-asp-net-mvc

If you are returning a RedirectResponse from your action method, it will send a 302 response back to the browser with the location header set to the url you want to redirect to and the browser makes a new GET request to that url. You will not be able to show an alert before the new GET request.
What you can do is, show the alert message in page rendered by the redirect.
[HttpPost]
public ActionResult Save(SomeViewmodel model)
{
TempData["Msg"] = "Some message";
return RedirectToAction("Index");
}
Now in the view returned by Index action, you can directly access TempData. No need to read and pass it via ViewBag again in Index action.
<script>
var msg = '#TempData["Msg"]';
if (msg.length) {
alert(msg);
}
</script>

Related

Use single post action method for multiple views

I have an sms sms application, which has different templates that users can use to send their SMSs, I used different view to represent different templates, however I want to use one action method to send an SMS, i might be different templates but at the end of the day user will send an sms which has two parameters, message itself and cell number to which this sms is to be send to,
Here is my 3 templates for now
[HttpGet]
public ActionResult BookingServiceReminder()
{
return View();
}
[HttpGet]
public ActionResult ServiceReminder()
{
return View();
}
[HttpGet]
public ActionResult DetailedReminder()
{
return View();
}
[HttpPost]
public ActionResult SendSMS(string message, string cellNumber)
{
if (_dbManager.SendSMS(cellNumber, message, User.Identity.Name))
{
TempData["Success"] = "Message was successfully sent";
}
else
{
TempData["Error"] = "An error occured while sending the message";
}
return RedirectToAction("BookingServiceReminder");
}
My question is, is there a way of using one method for all this views, I dont want to have multiple post methods which will almost have same code except the redirectToAction on which I want to return then current view(current template).
Yes, you can.
You would need to keep track someway that at which action user should be redirected after sending the sms.
One way for it is you can pass the flag back to view and post it as hidden field to determine that which action should be redirected to :
[HttpGet]
public ActionResult BookingServiceReminder()
{
ViewBag.RedirectTo = "BookingServiceReminder";
return View();
}
[HttpGet]
public ActionResult ServiceReminder()
{
ViewBag.RedirectTo = "ServiceReminder";
return View();
}
[HttpGet]
public ActionResult DetailedReminder()
{
ViewBag.RedirectTo = "DetailedReminder";
return View();
}
and in View you can have a hidden field which would post to action :
<input type="hidden" value="#ViewBag.RedirectTo" name="RedirectTo">
and in action add a new paramter:
[HttpPost]
public ActionResult SendSMS(string message, string cellNumber,string RedirectTo)
{
if (_dbManager.SendSMS(cellNumber, message, User.Identity.Name))
{
TempData["Success"] = "Message was successfully sent";
}
else
{
TempData["Error"] = "An error occured while sending the message";
}
return RedirectToAction(RedirectTo);
}

Render partial view or view from controller

I'm trying to render from controller either PartialView or View depending on condition coming from outside. Web-site fails on posting data with StackOverflowException.
Controller code:
public ActionResult Login(bool partial = false)
{
if (partial)
{
ViewBag.Partial = true;
return PartialView();
}
return View();
}
[HttpPost]
public ActionResult Login(UserViewModel userViewModel)
{
if (!ModelState.IsValid)
return View(userViewModel);
// some authrorization staff
}
Login.cshtml:
#using SK.DDP.ImageGallery.Helpers
#model SK.DDP.ViewModels.UserViewModel
#{
ViewBag.Title = "Login";
if (ViewBag.Partial != null)
{
Layout = string.Empty;
}
}
#*Some form staff*#
AuthorizationInfo.cshtml:
#{
Layout = string.Empty;
}
#{ Html.RenderAction("Login", "Authorization"); }
Template:
#*Some dif for form requested by menu*#
#{ Html.RenderAction("AuthorizationInfo", "Authorization"); }
I have a web-site with login page and login popup window appearing when user clicks on menu, so i wanted to reuse the same action of controller and code and app is continuing to fail with stackoverflow exception.
Thanks.
Seems its a bug in a Razor engine.
I workarounded it.
AuthorizationInfo.cshtml
#{ Html.RenderAction("LoginPartial"); }
AuthorizationController.cs
public ActionResult Login()
{
return View();
}
public ActionResult LoginPartial()
{
ViewBag.Partial = true;
return PartialView("Login");
}
Now Post of forms does not generate overflows with templates applied recursively.

MVC 5 - How to avoid data being visible in the address bar on Form Submit

It may be a stupid question because when I googled I can't find anybody asking this question. But my requirement is to not to show any values in the querystring. The below url shows Name and Password values in clear text, but I don't want it to show.
http://localhost:30813/Home/Index/0?Name=test&Password=test
Model:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
//public SecureString Password { get; set; }
public string Password { get; set; }
}
LoginController's view:
#using (Html.BeginForm())
{
<div>#Html.Label("Username:")</div>
<div>#Html.TextBoxFor(model => model.Name)</div>
<div>#Html.Label("Password:")</div>
<div>#Html.PasswordFor(model => model.Password)</div>
<div>
<input type="submit"
class="btn btn-primary"
value="Login"
style="line-height:normal!important;" />
</div>
}
LoginController.cs
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(User user)
{
if (IsValidUser(user.Name, user.Password))
{
return RedirectToAction("Index", "Home", user);
}
return View();
}
You are not very clear on what you want to do. You are also unclear on what you Index action method looks like. So I am going to go on what I see in front of me.
When you do this..
return RedirectToAction("Index", "Home", user);
..you are redirecting to an HTTP GET action method named Index in the Home controller. It is unclear to me if this is an empty action method or an action method that needs a User instance.
If you do not need a User instance then you can just do this:
return RedirectToAction("Index", "Home");
If you do require a User instance and you do not want to pass the values as parameters in the URL, then I suggest you do something like this (it is not my ideal way of doing it):
[HttpPost]
public ActionResult Index(User user)
{
if (IsValidUser(user.Name, user.Password))
{
TempData["user"] = user;
return RedirectToAction("Index", "Home");
}
return View();
}
And then your Home controller's Index action method will look something like this:
public ActionResult Index()
{
User user = (User)TempData["user"];
return View();
}
Now you have an instance of your user variable and can be used accordingly.
I hope this helps.
As other pointed out, when you pass some data in GET requests, that is passed using querystring. So if you don't want to show the user specific data there, you cannot keep the user object in the RedirectToAction().
One way you can achieve such functionality is to use Session variables:
In LoginController:
if (IsValidUser(user.Name, user.Password))
{
Session.Add("user",user);
return RedirectToAction("Index", "Home");
}
return View();
And in HomeController,Index():
public ActionResult Index()
{
User user = (User)Session["user"];
Session.Remove("user");
//other logics here
return View();
}
An alternative which I use for more control of my user experience is to post my form data via ajax.
Its a bit more PT but it allows me to interact with the user more in that I can easily show a progress indicator or quickly report an error message:
#using (Html.BeginForm())
{
<div>#Html.Label("Username:")</div>
<div>#Html.TextBoxFor(model => model.Name)</div>
<div>#Html.Label("Password:")</div>
<div>#Html.PasswordFor(model => model.Password)</div>
<div>
<input type="button"
class="btn btn-primary"
value="Login"
style="line-height:normal!important;" />
</div>
}
<script>
$('form button').on('click', function() {
Login();
});
function Login() {
$('#progress').show(); // indicate work is being done
var Name = $('#Name').val();
var Password = $('#Password').val();
var User = { "Name": Name, "Password": Password };
$.post('/Home/Login', User, function(data) {
if (!data.IsOk) {
$('#progress').hide(); // work complete, stop showing progress
alert(data.Message); // show an error message from the controller
}
else {
location.href = '#Url.Content("~/")';
}
});
}
</script>
This is over-simplified. You can do a lot more. As in my case, I'm displaying MDL dialogs that have the error message and titles on them.
The controller looks something like this:
[HttpPost]
public JsonResult Login(UserViewModel user)
{
try
{
if (IsValid(user))
{
generateCookie(user);
return Json(new { IsOk = true }, JsonRequestBehavior.AllowGet);
}
else
{
return Json(new { IsOk = false, Message = "Invalid user credentials" }, JsonRequestBehavior.AllowGet);
}
}
catch (Exception ex)
{
return Json(new { IsOk = false, Message = ex.Message }, JsonRequestBehavior.AllowGet);
}
}
Once again, I've over-simplified this so as to give you the gist of it. The Controller handles all of the backend logic and database querying, and the View takes care of presentation.
If the user is valid, the Controller will return a true bool value to the View which will then redirect back to the home page which is catered to show dynamic content.
On a final note, its worth mentioning that MVC comes with built-in login functionality which is probably a lot more secure that writing our own. I'm not exactly sure how to use it though and I have little time available to figure it out but its something you might want to investigate.

MVC Displaying error messages

I am trying to find a way to pass error messages back to my view from my controller.
public ActionResult Index()
{
ViewBag.splittingFileMessage = "Splitting File...";
ViewBag.startBatchMessage = "Start Batch Processing : ";
return View();
}
[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
var numberRecordsPerFile = int.Parse(ConfigurationManager.AppSettings["NumberRecordsPerFile"]);
var inputFileFolder = ConfigurationManager.AppSettings["InputFileFolder"];
var batchSplitterClient = new SplitterServiceClient();
var batchSplitterResponse =
batchSplitterClient.SplitFile(new BatchSplitterRequest
{
FilePath = inputFileFolder,
NumberOfRecordsPerFile = numberRecordsPerFile
});
var numberOfMessages = batchSplitterResponse.NumberOfMessages;
if (batchSplitterResponse.Result.ResultCode == 200)
{
return RedirectToAction("Progress");
}
else
{
ViewBag.error = "test error";
return RedirectToAction("Index");
}
}
So you can see in my Httppost method I am trying to set an error message i.e. ViewBag.error if it occurs in my WCF call.
But in my view on initial load ofcourse this hasn't been set.
I my view I have
<div class="errors">
<%: ViewBag.error %>
</div>
But on initial page load the error comes up on ViewBag.error
The call is ambiguous between the following methods or properties: 'System.IO.TextWriter.Write(string, params object[])' and 'System.IO.TextWriter.Write(char[])'
[HandleError(View = "Error")]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
int u = Convert.ToInt32("");// Error line
return View();
}
}
HandleError Attribute at Action Method Level
[HandleError(View = "Error")]
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
int u = Convert.ToInt32("");// Error line
return View();
}
Defining HandleError Attribute at Global Level
We can also apply the HandleError Attribute at the global level by registering it in the Global.asax in Application_Start event.
Example
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute(), 2); //by default added
filters.Add(new HandleErrorAttribute
{
View = "Error"
}, 1);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Error Page
#model System.Web.Mvc.HandleErrorInfo
#{
ViewBag.Title = "Error";
}
<h2>
Sorry, an error occurred while processing your request.
<br />
Error :
</h2>
<p>
#Model.Exception
</p>
Enabling Custom Error Handling
To enable custom error handling in the application by the HandleError attribute, we must add a CustomErrors element within the system.web section of web config.
Output
Catch Specific Type of Error
The ExceptionType property can help us to catch a specific type of error. For example, if we want to catch only an ApplicationException type of exception then we can define the ExceptionType.
[HandleError(ExceptionType = typeof(ApplicationException), View = "Error")]
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
int u = Convert.ToInt32(""); // Error line
return View();
}
User HandleErrorAttribute and Error Page or Elmah for Validation errors add to Modelstate and Use Error Page to look for Validation errors and display
If it answered your question, tick right mark on left side , to make it answered
Try casting it as a string
<div class="errors">
<%: (string)ViewBag.error %>
</div>
or ViewBag.error.ToString()

MVC3 two partial views in one view

I am trying to learn MVC 3 & Razor and I'm stuck here for 3 hours now. This is what I have
MVC project created using the default template with Account registration and all that good stuff from the template. What I'm trying to do is have both registration page and login page in the index of the HomeController so I created a partial view for both Register (_RegisterPartial) and LogOn (_LogOnPartial). When I go to the index page, I see the registration and login forms which is good but when I try to login or register it goes into an infinite loop.
My HomeController looks like this;
// **************************************
// Registration
// **************************************
public ActionResult DoRegister()
{
return PartialView("_Register");
}
[HttpPost]
public ActionResult DoRegister(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus = MembershipService.CreateUser(model.UserName, model.Password, model.Email, model.UserProfile);
if (createStatus == MembershipCreateStatus.Success)
{
FormsService.SignIn(model.UserName, false); // createPersistentCookie
return View("Success");
}
else
{
ModelState.AddModelError("", AccountValidation.ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
ViewBag.PasswordLength = MembershipService.MinPasswordLength;
return View(model);
}
// **************************************
// Login
// **************************************
public ActionResult DoLogin()
{
return PartialView("_Login");
}
[HttpPost]
public ActionResult DoLogin(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
// logged in
FormsService.SignIn(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl))
{
Redirect(returnUrl);
}
else
{
View("Success");
}
}
else
{
// Not logged in
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View("Success");
}
and my cshtml looks like this;
#{
ViewBag.Title = "Home Page";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#if (Request.IsAuthenticated)
{
#Html.ActionLink("Log Off", "LogOff", "Account")
}
else
{
Html.RenderAction("DoLogin");
Html.RenderAction("DoRegister");
}
Regards,
Ryan
Do you read exception messages?
A public action method 'Register' was not found on controller 'AudioRage.Controllers.HomeController'
Now look at the code of HomeController you've posted. Do you see a Register action on it? I don't.
So add one:
public ActionResult Register()
{
...
}
In your HomeController you have an action called Register but action is only accessible through POST verbs as it is decorated with the [HttpPost] attribute:
[HttpPost]
[ActionName("Register")]
public ActionResult Index(RegisterModel model)
so you cannot invoke it with a GET verb on /Home/Register.
I can't exactly replicate your situation but I would say that you partial forms are not posting correctly. Have a look a rendered html of the page and check where the forms are being posted to. My guess is that they are being posted to the Index action. Coupled with the redirect, I think that is where the infinite loop is coming from.
My guess is the html rendered for both forms are similar and posting to the same action i.e. <form action="/" method="post">, since they are being rendered by the HomeController's Index action.
Change the partial forms (_Login.cshtml and _Register.cshtml) and explicitly state which action/controller combination to post to (more on Html.BeginForm from MSDN)
#using (Html.BeginForm("DoLogin","Home")) {/*snipped*/} //in _Login.cshtml
#using (Html.BeginForm("DoRegister","Home")) {/*snipped*/} //in _Register.cshtml
Also I would change the Html.RenderAction calls to
Html.RenderPartial("_Login");
Html.RenderPartial("_Register");

Resources