I working on my first ASP.net MVC project and and i got some problems using multi forms in same page.
First i have created 2 partial Class :
(*Register will allow user to register,
*Login it allow user to login.)
Then i used HTML.render to integrate them in my "Logpage".
So i have To uses 2 different Action. Like this one:
[HttpPost]
public ActionResult Login(LogModel.Login Model)
{
if (ModelState.IsValid)
{
if (LogModel.Login.Verifuser(Model.IDUser, Model.Password))
{
FormsAuthentication.SetAuthCookie(Model.IDUser, false);
if (LogModel.Login.IsAdmin(Model.IDUser, Model.Password))
{
return View("Admin/Index");
}
else
{
return View("Agence/Index");
}
}
else
{
ModelState.AddModelError("", "Invalide username or Password");
return View(Model);
}
}
return View(Model);
}
The problem that on error case i'm redirect to new Page(White page contain validation summary). So i'm wondring how to show this error message in my default page Logpage.
You can solve this with three actions and a complex model.
public class LoginOrRegisterViewModel
{
public Models.RegisterViewModel Register { get; set; }
public Models.LoginViewModel Login { get; set; }
}
[HttpGet]
public ActionResult Login()
{
return View("Login", new LoginOrRegisterViewModel());
}
[HttpPost]
public ActionResult Register(Models.LoginViewModel model)
{
if(!ModelState.IsValid)
return View("Login", new LoginOrRegisterViewModel(){ Register = model });
else
{
//TODO: Validate the user
//TODO: Write a FormsAuth ticket
//TODO: Redirect to somewhere
}
}
[HttpPost]
public ActionResult Login(Models.RegistrationViewModel model)
{
if(!ModelState.IsValid)
return View("Login", new LoginOrRegisterViewModel(){ Login = model});
else
{
//TODO: CRUD for registering user
//TODO: Write forms auth ticket
//TODO: Redirect
}
}
In your code, make sure that you set the action of the Form:
#model Models.LoginOrRegisterViewModel
#using(Html.BeginForm("Login", "Controller", FormMethod.Post, new { id = "loginForm"}))
{
#Html.EditorFor(m => Model.Login)
}
#using(Html.BeginForm("Register", "Controller", FormMethod.Post, new { id = "registerForm"}))
{
#Html.EditorFor(m => Model.Register)
}
It looks like you also need to call IsAdmin in the !Verifyuser branch, to have it return the correct view:
if (ModelState.IsValid)
{
if (LogModel.Login.Verifuser(Model.IDUser, Model.Password))
{
FormsAuthentication.SetAuthCookie(Model.IDUser, false);
if (LogModel.Login.IsAdmin(Model.IDUser, Model.Password))
{
return View("Admin/Index");
}
else
{
return View("Agence/Index");
}
}
else
{
ModelState.AddModelError("", "Invalide username or Password");
if (LogModel.Login.IsAdmin(Model.IDUser, Model.Password))
return View("Admin/Index", Model);
else
return View("Agence/Index", Model);
}
}
Currently, you're calling return View(Model); when you have a ModelState error, which returns the default view for the action, called "Login".
If you want multiple forms posted to a controller action simultaneously, create a JSON object with client JS that has a separate property for each form and give each of those properties a camel-case name that corresponds to a respective ProperCase parameter of your controller action. Then, send in a serialization of that JSON object to your controller:
let form1 = $('#my-form1-id');
let form2 = $('#my-form2-id');
let url = 'myArea/myController/myAction'
let viewData = {
controllerParameter1: form1,
controllerParameter2: form2
};
$.ajax({
url: url,
type: 'POST',
data: JSON.stringify(viewData),
dataType: "json",
beforeSend: function () {
$('#my-submit-button-id').html('<i class="fa fa-2x fa-cog fa-spin"></i>');
$('#my-submit-button-id').attr("disabled", "disabled");
},
success: function (res) {
alert("Submitted!");
},
error: function (status, err) {
alert("Failed!");
},
complete: function (status, err) {
$('#my-submit-button-id').html('Submit My 2 Forms');
}
});
That should work for a controller action w/ a signature similar to:
[HttpPost]
public virtual async Task<ActionResult> MyAction(ViewModel1 controllerParameter1, ViewModel2 controllerParameter2)
{
// do amazing things
}
Related
I'm developing a simple Custom Role-based Web Application using ASP.Net MVC, In my login Action, I'm creating a Profile session as below:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
using (HostingEnvironment.Impersonate())
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
var employeeProfile = AccountBal.Instance.GetEmployee(loginId);
Session["Profile"] = employeeProfile;
FormsAuthentication.SetAuthCookie(model.UserName, true);
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", #"The user name or password provided is incorrect.");
return View(model);
}
}
And I'm checking this or using this session in all Controller Actions as below:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateOrEdit(MyModel model)
{
var employee = (Employee) Session["Profile"];
if (employee == null)
return RedirectToAction("Login", "Account");
if (ModelState.IsValid)
{
// Functionality goes here....
}
}
Is there any way I can move this piece of session checking code in a base class or centralized class? so that, I do not need to check it every time in a Controller Actions instead I will access the properties directly
say,
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateOrEdit(MyModel model)
{
var employee = _profileBase.GetCurrentProfile();
if (employee == null)
return RedirectToAction("Login", "Account");
if (ModelState.IsValid)
{
// Functionality goes here....
}
}
Create a base controller that contains your GetCurrentProfile method to retrieve current user profile like
public class BaseController : Controller
{
public Employee GetCurrentProfile()
{
return (Employee)Session["Profile"];
}
public bool SetCurrentProfile(Employee emp)
{
Session["Profile"] = emp;
return true;
}
}
And inherit your desired controller with above BaseController and access your GetCurrentProfile method like below
public class HomeController : BaseController
{
public ActionResult SetProfile()
{
var emp = new Employee { ID = 1, Name = "Abc", Mobile = "123" };
//Set your profile here
if (SetCurrentProfile(emp))
{
//Do code after set employee profile
}
return View();
}
public ActionResult GetProfile()
{
//Get your profile here
var employee = GetCurrentProfile();
return View();
}
}
GetCurrentProfile and SetCurrentProfile directly available to your desired controller because we directly inherit it from BaseController.
You may usetry/catch in above code snippet.
Try once may it help you
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.
I am using custom Login for my application. I want to insert last login date and time in database and show last login date and time when user login into application.
Controller Code:
public class HomeController : Controller
{
MyDatabaseEntities db = new MyDatabaseEntities();
//
// GET: /Home/
public ActionResult Index()
{
if(Session["LoggedUserID"]!= null)
{
return View();
}
else
{
return RedirectToAction("Login");
}
}
public ActionResult Login()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(Login l)
{
if (ModelState.IsValid)
{
using(MyDatabaseEntities db = new MyDatabaseEntities())
{
this.UpdateLoginLastUpdateStatus();
var v = db.Logins.Where(a => a.UserName.Equals(l.UserName) && a.Password.Equals(l.Password)).FirstOrDefault();
if (v!= null)
{
Session["LoggedUserID"] = v.UserID.ToString();
Session["LoggeduserName"] = v.UserName.ToString();
Session["LastLogin"] = v.LastWebLogin.ToString();
return RedirectToAction("Index");
}
}
}
if(!ModelState.IsValid)
{
ModelState.AddModelError("","Username or Password is incorrect!!");
}
return View(l);
}
public void UpdateLoginLastUpdateStatus()
{
Login l = new Login();
l.LastWebLogin = DateTime.Now.ToString();
db.SaveChanges();
}
[HttpGet]
public ActionResult LogOff()
{
Session.Abandon();
return RedirectToAction("Login", "Home");
}
}
}
Index View
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#if (Convert.ToString(Session["LoggedUserID"]) != "")
{
<section>
<p class="toplink">#Html.ActionLink("Logout", "LogOff", "Home")</p>
</section>
}
Last Logged on: #Session["LastLogin"]
You have been logged in.
I am able to Log In and Log Out but this Last Logged In Time is not working.
The problem is this line is trying to update the login LastWebLogin before you've even retrieved it:
this.UpdateLoginLastUpdateStatus();
You need to call this AFTER you've assigned the Login to the variable v. Additionally, the method is creating a NEW Login class, not using an existing one. For that to work you'd have to pass a parameter v to the method. You don't even need that method really - unless you call it from multiple places and you want to avoid repeated code - it just needs to be a simple as:
var v = db.Logins.Where(a => a.UserName.Equals(l.UserName) && a.Password.Equals(l.Password)).FirstOrDefault();
if (v!= null)
{
// get previous login
var prevLogin = v.LastWebLogin;
// update with current login
v.LastWebLogin = DateTime.Now.ToString();
db.SaveChanges();
Session["LoggedUserID"] = v.UserID.ToString();
... etc
(MVC)How to RedirectToAction and keep the content of a Html.Textbox in MVC4? This is my controller how do I fix it and what goes in the view?
[HttpPost]
public ActionResult Index(string ssn)
{
var exist = false;
var record = _db.Citations.Where(u => u.SSN == ssn).FirstOrDefault();
if (record != null)
{
exist = true;
return RedirectToAction("EditDetails", new { id = record.CitationID });
}
else
{
return RedirectToAction("Index", "SubmitAward", ssn); // wiped out ssn! I need to keep the ssn
// so that the user can fill out the form.
}
If your get action is like this:
public ActionResult Index(string ssn)
{
.........
..........
.........
}
then do like this:
return RedirectToAction("Index", "SubmitAward", new { ssn = ssn});
but if it is the same index view of post action which is posted in question, you can pass the object back to view if your view is strongly typed to this class:
return View(record);
Try below code
return RedirectToAction("Index", "SubmitAward", new { ssn = ssn});
And your redirect action will be
public ActionResult Index(string ssn)
{
ViewBag.SSN=ssn;
return View(record);
}
And your view will contain textbox like
#Html.TextBox("SSN",ViewBag.SSN)
It may helps you..
I will have a registration form on my website which will firstly show the boardrules / legal. Once accepted it will then show the main registration form. Using a ViewModel as below:
public class MyViewModel
{
public int Readrules { get; set; }
public int Coppa { get; set; }
}
public ActionResult Register(MyViewModel model)
{
... at this stage model.Readrules and model.Coppa will contain the values passed
as query string parameters tat you could use here
}
The idea is if I go to /register it will show the rules and then /register?readrules=1 it will then show the registration form. This is how it was done in PHP but now I am migrating to ASP.NET..
What is the best way of doing this? Can I redirect to the same action and just parse the value of model.ReadRules or must I use more than one action? I would prefer to keep this in one action and just check if model.ReadRules == 1 and either display the boardrules or registration form.
Thanks
Instead of re-using the Register action, you could have different controller actions for displaying the rules, registering and processing the registration, like so:
Controller Actions:
public ActionResult BoardRules()
{
return View();
}
public ActionResult Register(MyViewModel model)
{
if (model.ReadRules != 1)
return RedirectToAction("BoardRules");
return View();
}
public ActionResult Registration(MyViewModel model)
{
if (model.ReadRules != 1)
return RedirectToAction("BoardRules");
//Process the registration
return View();
}
Views:
BoardRules.cshtml:
#* HTML Displaying Rules *#
Accept Rules
Register.cshtml:
#using (Html.BeginForm("Registration", "[Controller Name]", new { ReadRules = 1 }))
{
#* Form Fields *#
<input type="submit" value="Process Registration" />
}
Registration.cshtml
<h2>Congratz on Registering!</h2>
public ActionResult Register()
{
return View("boardrules"); //Default
}
public ActionResult Register(MyViewModel model)
{
if (model.ReadRules == 1)
{
model.ReadRules++; //Next time it won't be 1 but step 2
return View("registration",model);
}
else
{
//Do IF or Case for other step
}
return View("boardrules"); //Default
}