I have this html in my asp.net MVC 4 application:
#if (User.Identity.IsAuthenticated)
<li class="login-link"> {
#Html.ActionLink(User.Identity.Name, "LogOff", "Account")
</li>
}
and this controller action
//[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
WebSecurity.Logout();
return RedirectToAction("Index", "Home");
}
but when i click log out link, user doesnt log out.
Please suggest how to fix it
In your code sample above, you wrote:
#if (User.Identity.IsAuthenticated)
<li class="login-link"> {
#Html.ActionLink(User.Identity.Name, "LogOff", "Account")
</li>
}
Based on how your #if (User.Identity.IsAuthenticated) statement resolves this may lead to some wonky code being rendered for the browser.
Does the following Razor code more correctly reflect your intent?
#if (User.Identity.IsAuthenticated)
{
<li class="login-link">
#Html.ActionLink(User.Identity.Name, "LogOff", "Account")
</li>
}
I couldn't find anything wrong with your code.
But try this and let me know if it works for you:
cshtml :
#Html.ActionLink("Log Off", "LogOff", "LogOn", null, new { #class = "actnclass" })
controller :
public ActionResult LogOff()
{
Request.Cookies.Remove("UserId");
FormsAuthentication.SignOut();
return RedirectToAction("LogOn", "LogOn");
}
Your Razor code looks good. Even a basic html logout code like the one below should do
Logout
Keep a breakpoint in your LogOff action in your Account controller and see what is happening. You seem to be using WebSecurity.Logout(); to logout and then redirect the user to the Home/Index page.
Also would like to know by looking at what are you inferring that user has not logged out.
Nothing wrong with you code. The point is the Action LogOff in Account is HttpPost Method, so it will only accept from a form submit.
You have to 3 ways to achieve Log off functionality
Change LogOff Method to HttpGet instead of HttpPost in Account Controller
Add an HttpGet overload for it take one parameter to leave the HttpPost method in Account Controller
[HttpGet]
public ActionResult LogOff(string token) {
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Index", "Home");
}
// Already Exists
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff() {
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Index", "Home");
}
Use Javascript to submit the LogOff form
#using Microsoft.AspNet.Identity
#if (Request.IsAuthenticated)
{
using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" }))
{
#Html.AntiForgeryToken()
// ...
LogOff
}
}
Related
I have a basic MVC form that allows a user to submit a zip code and after clicking submit, the user should be redirected to a new view. My code seems to redirect to the next action successfully. However after the redirect, the controller returns back to the original action, so to the user, the page next changed at all.
Here's my View code:
#using (Html.BeginForm("PricingQuote", "Home", FormMethod.Post, new { #class = "rd-mailform text-center offset-top-30" }))
{
<div class="form-group">
<label class="form-label" for="contact-zip">Zip Code</label>
<input class="form-control" id="contact-zip" type="text" name="zip" data-constraints="##Required">
</div>
<button class="btn btn-primary offset-top-30" type="submit">GET STARTED</button>
}
Here's the PricingQuote action in my HomeController. This action redirects to the Pricing action in my Pricing controller:
[HttpPost]
public ActionResult PricingQuote(string zipCode)
{
return RedirectToAction("Pricing", "Pricing");
}
Here's my PricingController:
public class PricingController : Controller
{
// GET: Pricing
public ActionResult Pricing()
{
return View();
}
}
So, after clicking GET STARTED, it accesses my Home/PricingQuote action. This action then tries to redirect to the Pricing/Pricing action, which it does however, the code then seems to (incorrectly) return back to Home/PricingQuote and exits the action.
Any idea how I can redirect & display my Pricing view?
Thanks
Pass the controller in as the second parameter:
[HttpPost]
public ActionResult PricingQuote(string zipCode)
{
return RedirectToAction("Pricing", "PricingController");
}
Thanks for your responses. I was able to figure out my problem. The name of the action I was trying to redirect to ("Pricing") was the same name as my controller ("Pricing"). As a test, I renamed my action to "PricingA" & it worked, so apparently based on this, an action cannot be the same name as the controller when attempting a "RedirectToAction", which I was unaware of (at least that's my assumption based on the results I've found).
Unfortunately, I tried googling for some add'l evidence of this, to provide with this answer, but was unable to find any.
This works:
HomeController:
[HttpPost]
public ActionResult PricingQuote(string zipCode)
{
return RedirectToAction("PricingA", "Pricing");
}
Pricing Controller
[HttpGet]
public ActionResult PricingA()
{
return View();
}
I have a view includes login and registration form.
LoginAndRegister.cshtml file:
#model Tuple<Models.LoginViewModel, Models.RegisterViewModel>
#using (Html.BeginForm("Login", "Account", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false)
// Form Login here
}
#using (Html.BeginForm("Register", "Account", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false)
// Form Register here
}
AccountController file:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register([Bind(Prefix = "Item2")] RegisterViewModel model)
{
if (ModelState.IsValid)
{
// enter code here
}
// If we got this far, something failed, redisplay form
var tupleModel = new Tuple<LoginViewModel, RegisterViewModel>(null, model);
return View("LoginAndRegister", tupleModel);
}
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login([Bind(Prefix = "Item1")] LoginViewModel model, string returnUrl)
{
var tupleModel = new Tuple<LoginViewModel, RegisterViewModel>(model, null);
if (!ModelState.IsValid)
{
return View("LoginAndRegister", tupleModel);
}
}
I have 2 question, if you guys don't mind could you help me?
When I pass model (just one item of tuple) from controller to View, i have to change it to Tuple<> type and pass 1 value is null. Does this way is correct? It's working for me but I afraid that my way isn't correct.
And then, when model is invalid (example: values's input in to Login form is invalid), error messages will bind into #Html.ValidationSummary(false). But it's showed in 2 places (register and login form). How to resolve this issue?
https://gyazo.com/e9146059a6a098ee787565222d8dc744
Thanks for kind helping
Login and register are two different models. You can get around using Tuples in asp.net with the html helpers. Using a tuple just makes things messy.
What you probably want is something like this:
Register.cshtml file:
#model Models.RegisterViewModel
#using (Html.BeginForm("Register", "Account", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false)
// Form Register here
}
<div>Or if you already have an account then login:</div>
#Html.RenderAction("Login")
Controller:
public ActionResult Login()
{
if (Request.IsAjaxRequest())
{
return PartialView();
}
}
This will render the login view in the register view, you can also do this the other way around. Although I'd personally just offer a link to the user to redirect them to login page rather than using renderaction.
I'm trying to create contact us page where user fill's in the detail and submit and at the bottom display message which comes from server.
The way i have implemented is something like this.
[HttpGet]
public ActionResult ContactUs()
{
//Process the stuff
return View("~Views/Contact/Contact.cshtml", model)
}
now when page load it hits above method and display form with the layout including header and footer.
Once user submits form it hits below method
[HttpPost]
public ActionResult ContactUs(ContactUs form)
{
//Process the stuff
View.Message="Thank you for your enquiry."
return View("~Views/Contact/Contact.cshtml", model)
}
It returns to the same page but it doesnt render the body layout not even header or footer simply display outputs form.
Not sure what im doing wrong there, is there any better approach ?
Thanks
Based on the code above, I believe you're attempting something like:
public class UxController : Controller
{
public ActionResult WithResponse(ActionResult result, string message)
{
PageResponse(message);
return result;
}
protected void PageResponse(string message)
{
TempData["Ux_Response"] = message;
}
}
That would be your Controller, then the Controller for that specific page, it would look like:
public class HomeController : UxController
{
public ActionResult Index()
{
return View();
}
public ActionResult SubmitForm(string message)
{
return WithResponse(RedirectToAction("Index"), "Thank you for feedback.");
}
}
Then in your front-end code, you would do the following:
#if(TempData["Ux_Response"] != null)
{
<div>#TempData["Ux_Response"]</div>
}
<form action="/Home/SubmitForm" method="post">
<input type="text" name="message" />
<input type="submit" value="Submit" />
</form>
Obviously you could enhance this, with more versatility. However, you're relying on Post, which will cause a screen flicker. So the better route, may be to do Ajax, then return a JsonResult. Hopefully this helps you out.
It should work if you change your controller/view like this.
Controller;
public ActionResult Contact(ContactModel model)
{
ViewBag.Message = "Your contact page.";
return View(model);
}
public ActionResult SaveContact(ContactModel model)
{
//process values in your model and then rest model
ContactModel.Message = "Thank you for contacting us"; //show thank you message
return RedirectToAction("Contact",model);
}
View;
#model MvcApplication1.Models.ContactModel
#{
ViewBag.Title = "Contact";
}
#using (Html.BeginForm("SaveContact", "Home", Model, FormMethod.Post))
{
#Html.DisplayFor(m => m.Message);
<button type="submit">Submit</button>
}
I manged to solve this. the issue was the because i was using sitecore cms the form action wasnt processing it full work flow, after i removed the action, it defaults to action method which defined in cms and triggers the cms workflow.
I'm trying to setup a simple form submission in MVC5, but I'm finding that my method doesn't get fired unless I have both an ActionLink and submit button.
I have a simple model:
public class LoginModel
{
public string username { get; set; }
}
Then I have two methods in my controller, one for when a form submission is available and one when not:
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel myModel)
{
var username = myModel.username;
// do something with username
return View();
}
Finally, my View creates a POSTing form:
#using (Html.BeginForm("Login", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.TextBox("username", string.Empty)
#Html.ActionLink("Enter", "Login")
<input type="submit" name="submit" value="Enter" />
}
I don't really care whether I use an ActionLink or whether I have a submit button (MSDN implies it should be the latter), but if I have only one of them, my [HttpPost] method is not called, and my page is redirected with the username in the query string:
/Home/Login?ReturnUrl=%2F%3Fusername%3DmyUsernameHere
If I have both on the page, I can click the ActionLink and I see that the appropriate method is called with myModel.username containing the value I provided. The submit button, however, will still redirect.
I want this form method to be POST, not GET (which it is in the generated HTML), and for failures to not contain the key as a GET param. What's the problem with having only one of these form submission mechanisms? Why do they not trigger the POST as expected? Is there something more I need to do to 'register' my model with the view, even though it is submitted properly in my workaround scenario?
Edit -- My configured routes are typically as follows:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.RouteExistingFiles = true;
So normally, I'm going to /Login instead of /Home/Login.
Also, I do have some authentication setup, with my HomeController decorated with [Authorize] and my Login methods both with [AllowAnonymous]. When I remove all annotations, I still find that my [HttpPost] is not called, and username shows up as a GET parameter instead of being POSTed.
I believe that the application doesn't understand that you're trying to make a model with just username. So, you are sending a string username and attempting to place it into a model. I'm not entirely sure about that. Could you try binding your form to your model? Here's an example:
View:
#model YourApplication.Models.LoginModel
#using (Html.BeginForm("Login", "Home"))
{
#Html.AntiForgeryToken()
#Html.TextBoxFor(model => model.username, string.Empty)
<input type="submit" value="Enter" />
}
Controller:
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login([Bind(Include="username")] LoginModel myModel)
{
var username = myModel.username;
// do something with username
return View("Congrats");
}
Here's an alternate option. If you wanted to do something else, maybe try accepting the string "username" and creating your model after? Example:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(string username)
{
LoginModel myModel = new LoginModel();
myModel.username = username;
// do something with username
return View("Congrats");
}
Either way, you should be using the submit button.
My task is create both login and registration form on one view!
I have home view. On a home view I have two forms, login and register. They rendered by #Html.RenderPartial(partial view name, model). That forms relates to Account controller (Login and Register actions).
Let me first provide you with some code…
Models:
// Login model, that contains email and password
public class Login
{
…
}
// Registration model that contains minimum user data, e.g. name, surname
public class Register
{
...
}
// Model for home view which contains models for both register and login
public class Home
{
public Login LoginModel { get; set; }
public Register RegisterModel { get; set; }
}
Views:
Index.cshtml
#model App.Models.Home.Home
<!-- Login Box -->
#{ Html.RenderPartial("_LoginArea", Model.LoginModel); }
<!-- Register Box -->
#{ Html.RenderPartial("_RegisterArea", Model.RegisterModel); }
_LoginArea.cshtml
#model App.Models.Account.Login
<div style="float: left; margin-right: 100px;">
<h1>#Localization.Account.Login</h1>
<!-- Login form -->
<div id="loginBox">
#using (Html.BeginForm("Login", "Account", FormMethod.Post))
{
<div class="editor-label">
#Html.LabelFor(m => m.Email):
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Email)
#Html.ValidationMessageFor(m => m.Email)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Password):
</div>
<div class="editor-field">
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(m => m.Password)
</div>
<div class="editor-label">
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe)
</div>
<p>
<input type="submit" value="#Localization.Account.Login" />
</p>
}
</div>
</div>
_RegisterArea.cshtml
#model App.Models.Account.Register
<div style="vertical-align: bottom;">
<h1>#Localization.Account.Register</h1>
<div>
#using (Html.BeginForm("Register", "Account"))
{
//same things like in login
}
...
Controllers:
HomeController
//
// GET: /Home/
public ActionResult Index(Home model)
{
//
// If logedin redirect to profile page
// Else show home page view
//
if (Request.IsAuthenticated)
{
return RedirectToAction("Index", "User", new { id = HttpContext.User.Identity.Name });
}
else
{
model.LoginModel = new Login();
model.RegisterModel = new Register();
return View(model);
}
}
Just show you Login action from Account controller
//
// POST: /Account/Login
[HttpPost]
public ActionResult LogIn(Login model)
{
if (Request.IsAuthenticated)
{
return RedirectToAction("Index", "User", new { id = HttpContext.User.Identity.Name });
}
else
{
if (ModelState.IsValid)
{
if (model.ProcessLogin())
{
return RedirectToAction("Index", "User", new { id = HttpContext.Session["id"] });
}
}
}
//Note: this is problem part
// If we got this far, something failed, redisplay form
return View("~/Views/Home/Index.cshtml", model);
}
So everything works fine, BUT! When model state is not valid I have following exception
The model item passed into the dictionary is of type 'App.Models.Account.Login', but this dictionary requires a model item of type App.Models.Home.Home'.
I can bind that partial views and account actions to Home model, but this is not what I need.
I planning to use _LoginArea.cshtml view in other views (for example in a page header of ‘User’ view, and that view has another model)
So, I need action method (e.g. Login) to retrieve Login model, not Home or whatever else. But in this case I’m unable to return model to Home view.
How can I solve this issue? What is a best proper way?
Sorry for a lot of code, just want clear things.
In this case I would recommend having a central login/registration page where users will be redirected to when validation fails. For the purpose of example, let's say this is a view named SiteAuthenticate
So in the case of your LogIn(Login model) action, if validation fails, you would always return the SiteAuthenticate view which would be strongly typed to include the Login and Register models, very similar to your Home model.
That last catch to make this approach work would be to add a ReturnUrl property to your Login and Register models. This could be populated however you wish, or using something like Request.UrlReferrer. This way you'll know where to send the user back to when they eventually login/register successfully.
So, when all is said and done, your LogIn action might look something like this:
[HttpPost]
public ActionResult LogIn(Login model)
{
if (Request.IsAuthenticated)
{
return RedirectToAction(model.ReturnUrl);
}
else
{
if (ModelState.IsValid)
{
if (model.ProcessLogin())
{
return RedirectToAction(model.ReturnUrl);
}
}
}
// If we got this far, something failed. Return the generic SiteAuthenticate view
var saModel = new SiteAuthModel { LoginModel = model };
return View("~/Views/Account/SiteAuthenticate.cshtml", saModel);
}
Your SiteAuthenticate would also then post back to the same Login action. Since you've passed along the original RetunrUrl, if login is then successful, the use will be redirected to their original destination as planned.
Hope that helps, let me know if anything is unclear or doesn't make sense.
SOLVED!!! Used Html.RenderAction instead.
Here is code:
Index.cshtml
<!-- Login form -->
#{ Html.RenderAction("Login", "Account"); }
<!-- Register form -->
#{ Html.RenderAction("Register", "Account"); }
Partial views are same…
Controller’s actions:
//
// GET: /Account/Login
public ActionResult Login()
{
Login model = new Login();
if (TempData.ContainsKey("Login"))
{
ModelStateDictionary externalModelState = (ModelStateDictionary)TempData["Login"];
foreach (KeyValuePair<string, ModelState> valuePair in externalModelState)
{
ModelState.Add(valuePair.Key, valuePair.Value);
}
}
return View("_LoginHome", model);
}
//
// POST: /Account/Login
[HttpPost]
public ActionResult Login(Login model)
{
if (Request.IsAuthenticated)
{
return RedirectToAction("Index", "User", new { id = HttpContext.User.Identity.Name });
}
else
{
if (ModelState.IsValid)
{
if (model.ProcessLogin())
{
return RedirectToAction("Index", "User", new { id = HttpContext.Session["id"] });
}
}
}
TempData.Remove("Login");
TempData.Add("Login", ModelState);
// If we got this far, something failed, redisplay form
return RedirectToAction("Index", "Home");
}
Same things for Register actions.
As u can see I’m using TempData to store model state, and then retrieve it when action is begin render.
Other question is how we can know where we should come back if model state is invalid. My solution is to parse Request.UrlReferrer, like ataddeini said.
Big thanks to my friend Danylchuk Yaroslav for him help!