I have used the MVC template in an application and modified it as I need. On Logout button click, I should be able to reach the logout action where I define my logout logic. But when I click the button, it directly takes me to the login screen without hitting the logoff action in the controller.
I tried adding an action link and specifying explicitly to reach the logoff action and it did not work either.
If I remove this action, it throws an error that it's not found. I checked in the fiddler tool and this action is called on the button click and redirected to Login, but the breakpoint is never hit or if I change the content inside the logoff action, it is not reflected.
I also tried using a different name for the same action, no change in the behavior was found.
Here is the view and controller action.
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
//AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Login", "Account");
}
The partial view embedded in layout:
#if (1==1)
{
using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" }))
{
#Html.AntiForgeryToken()
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<p class="navbar-text">Allturna's Insights Portal</p>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>#Html.ActionLink("Home", "Index", "Home")</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>
<p class="navbar-text">Hello!</p> #*Use username here from viewdata*#
</li>
<li>
<li>
#Html.ActionLink("Tenants", "GetTenant", "Home", new { ReturnUrl = ViewBag.ReturnUrl }, null)
</li>
<li>Log off</li>
<li><img src="~/Content/Image/Allturna_logo.png" height="50" width="220" /></li>
</ul>
</div>
}
}
The RouteConfig.cs is as below:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Login", id = UrlParameter.Optional }
);
}
}
I have not been able to figure out why the logoff button click/form submission can not reach the logoff action. I would appreciate any help.
As I am working on it, I wanted to add some additional notes. If I use the default aspnet template with identity, it reaches the logoff button. But I am trying to implement my own login and logout logic without using Identity. That's when it does not hit the logoff.
I have posted both logic (with/without identity) hits the logoff as expected.
The following with Identity works:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
//Add tenantId to session from user info (temporary testing with Allturna)
Session["TenantId"] = 1;
Session["AccessToken"] = "my token";
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
when I use the following login logic (without identity), the Logoff does not get hit.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
//get the authentication URL
string strJwtToken = string.Empty;
string strAuthenticationUrl = ConfigurationManager.AppSettings.Get("AuthenticationServerEndPoint");
if (String.IsNullOrEmpty(strAuthenticationUrl))
throw new Exception("Authentication Endpoint is not specified.");
ServiceClient client = new ServiceClient(strAuthenticationUrl, "Authenticate");
dynamic objAuthentication = new ExpandoObject();
objAuthentication.UserName = model.Email;
objAuthentication.Password = model.Password;
client.AddJsonBody(objAuthentication);
HttpStatusCode httpReturnCode = client.Post(out strJwtToken);
if (httpReturnCode == HttpStatusCode.OK)
Session["AccessToken"] = strJwtToken;
return RedirectToLocal(returnUrl);
}
I would need to understand what does the identity framework/SignInManager does that my logic is missing and how I can add that to my logic and make the logout work correctly. Thanks in advance.
Related
I am doing my application in MVC. In my view i have one textbox named as EmailId, and one Submit button. If i enter the Email id and submit the button,the Label of the button want to change as Verify and the text box should be cleared without refreshing the page.
My view page is
<div class="sign" id="email">
#using (Html.BeginForm("Randa", "BU", FormMethod.Post))
{
<div class="sign1">
<div class="sign2" style="height:267px;width:562px;margin-left:214px" id="cdiv">
#Html.TextBox("EmailId","", new {#placeholder ="Enter the Email id",id="txtemail "})<br /><br />
<input type="submit" name="submit" value="Sign Up" id="btn" onclick="addbutton()" class="addbutton"/>
</div>
</div>
}
</div>
<div class="drnd" id="rnd" style="display:none">
#using (Html.BeginForm("Ra_verify", "BU", FormMethod.Post))
{
<div class="sign1">
<div class="sign2" style="height:267px;width:562px;margin-left:214px" id="cdiv">
#Html.TextBox("Getran", "", new { #placeholder = "Enter the Randam", id = "txtrnd" })<br /><br />
<input type="submit" name="submit" value="Verify" id="btnrnd" class="addbutton" />
</div>
</div>
}
</div>
}
<script type="text/javascript">
var btxt = "Verified";
document.getElementById("#btn").innerHTML = btxt;
</script>
<script type="text/javascript">
function addbutton()
{
($("#email").hide())
$("#rnd").show();
}
</script>
My controller code is
public ActionResult Randa()
{
return View();
}
[HttpPost]
// send the randam No to the Entered mail id . Store the mail id and randam no into tbl_bussiness table;
public ActionResult Randa(string EmailId, string submit)
{
string userId = System.Configuration.ConfigurationManager.AppSettings["UserTypeId"];
int typeid = Convert.ToInt32(userId);
if (ModelState.IsValid)
{
if (submit != null && EmailId != null)
{
EmailManager.SendConfirmationEmail(EmailId);
tbl_BusinessUser b = new tbl_BusinessUser();
b.EmailId = EmailId;
b.RandomNumber = (int)Session["rnd"];
b.UserTypeId = typeid;
b.CreateDTTM = System.DateTime.Now;
db.tbl_BusinessUser.Add(b);
db.SaveChanges();
ViewBag.message = "Please check ur Mail for randam no.Enter random in textbox ";
}
else
{
ModelState.AddModelError("", "Error");
}
}
return View();
}
public ActionResult Ra_verify()
{
return View();
}
[HttpPost]
// check the random no with table random no ,if match redirect to registration create page
public ActionResult Ra_verify(int EmailId, string submit)
{
if (submit != null)
{
// int c = Convert.ToInt32(EmailId);
tbl_BusinessUser b = new tbl_BusinessUser();
var tbra = db.tbl_BusinessUser.Where(x => x.RandomNumber == EmailId).FirstOrDefault();
//var tbram = Convert.ToInt32(tbra);
return RedirectToAction("Create", "BU");
}
return View();
}
Can anyone please help me?
Thanks in Advance.
We have to use Ajax whenever we want to update the value in the webpage without refreshing.
We have to do following things to make your page work.
Remove BeginForm block from your view because when we use BeginForm, it will send request to controller and refreshes the page.
Use Ajax to pass information to controller and update the page without refreshing it.
As you have two POST actions in controller, so keep both divs "rnd" and "email"
Here is sample script block with Ajax option to update the page as you requested,
$('#btn').click(function () {
var urlinfo = '/Home/Randa';
var textboxValue = $('#txtemail').val();
$.ajax({
type: "POST",
data: { value: textboxValue },
url: urlinfo,
success: function (result) {
$('#email').hide();
$('#rnd').show();
},
error: function () {
alert("failed");
}
});
});
First of all you need to use Ajax.BeginForm
Using Ajax.BeginForm with ASP.NET MVC 3 Razor
And on success function you can write the below code for clear text EmailId, and one Submit button.
$("#EmailId").val("");
$("#btn").val("Verify");
and you don't need two forms, if you are going to do the above.
I am working on an MVC5 application with C# back-end. I am attempting to get change /Controller/Action?parameter=value show as /Controller/Action/Value.
Currently I have an action link on a page setup in hopes to be able to view the user's profile info:
#{ var test = new RouteValueDictionary {{"username", "testman"}}; }
<h2>#ViewBag.Title</h2>
<div class="row">
<div class="col-md-2" style="border-right:1px solid #EEE; min-height: 250px;">
Sidebar 1
</div>
<div class="col-md-8">
test link
</div>
<div class="col-md-2" style="border-left:1px solid #EEE; min-height: 250px;">
Sidebar 2
</div>
</div>
And here is the Action:
[RequireRequestValue(new[] { "username" })]
[Route("Test/{username}")]
public ActionResult Test(string username)
{
var route = RouteData.Route;
try
{
ViewBag.Page = 9;
ViewBag.Title = "Testing";
ViewBag.Message = username;
return View();
}
catch (Exception ex)
{
var error = new HandleErrorInfo(ex, "Home", "Test");
ViewBag.errorMessage = ex.Message;
return View("Error", error);
}
}
I have added routes.MapMvcAttributeRoutes(); to my RouteConfig.cs file.
If I do not add the [Route("Test/{username}")] Attribute to my action, the link renders like this: Home/Test?username=testman. If I add the Attribute the link renders to my liking: Home/Test/testman.
Unfortunately, I get this server 500 error when I click on the link, and this is where I have been stuck:
No matching action was found on controller '<project>.Controllers.HomeController'.
This can happen when a controller uses RouteAttribute for routing, but no action on
that controller matches the request.
For fun I even tried creating an explicit ActionResult to see if I could get something different, but no luck there.
I am obviously missing something...can anyone help me get on the right track?
Thanks!
I'm trying to build an inbox that is very similar to facebooks message inbox, where you have a list of conversations(I only want a list of a message title) and when you click the conversation or message title in my situation, I want the whole message to be rendered next to it in a partial view.
Here's my Inbox view:
#model BlocketProject.Models.ViewModels.ProfilePageViewModel
#{
ViewBag.Title = "Inbox";
}
<h2>Dina meddelanden:</h2><br />
<div class="left">
<table id="messageTable">
#foreach (var message in Model.UserMessages)
{
<tr>
<td>
<button type="submit" class="messageButton">
#if (message.Unread == true)
{
<h4 style="font-weight:bold;">#message.MessageTitle</h4>
}
else if (message.Unread == false)
{
<h4>#message.MessageTitle</h4>
}
</button>
</td>
</tr>
}
</table>
</div>
<div class="right">
#Html.Partial("ReadMessage")
</div>
When I click this message-element that is a button, I want to pass that messageId to the PartialView ReadMessage:
#model BlocketProject.Models.DbClasses.DbMessages
<h2>#Model.MessageTitle</h2><br />
<p>#Model.MessageText</p>
and the controller looks like this:
[HttpPost]
public ActionResult Inbox()
{
var allMessages = ConnectionHelper.GetAllMessages(ConnectionHelper.GetUserByEmail(User.Identity.Name).UserId);
var model = new ProfilePageViewModel();
model.UserMessages = allMessages;
return View("Inbox", model);
}
[HttpPost]
public ActionResult ReadMessage(int messageId)
{
var model = ConnectionHelper.GetMessageByMessageId(messageId);
return PartialView("ReadMessage", model);
}
I've tried passing the messageId through a post as you can see in my controller, but then the partialView is returned as a new page and I simply want to render it in the Inbox view.
Any ideas?
EDIT:
Jonesy's answer fixed my problem when I edited it like this:
Controller:
public ActionResult ReadMessage(int messageId)
{
var model = ConnectionHelper.GetMessageByMessageId(messageId);
return PartialView("ReadMessage", model);
}
View:
<div class="left">
<table id="messageTable">
#foreach (var message in Model.UserMessages)
{
<tr>
<td>
#using (Ajax.BeginForm("ReadMessage", new { #messageId = message.MessageId }, new AjaxOptions { UpdateTargetId = "showMessage" }, FormMethod.Post))
{
<button type="submit" class="messageButton">
#if (message.Unread == true)
{
<h4 style="font-weight:bold;">#message.MessageTitle</h4>
}
else if (message.Unread == false)
{
<h4>#message.MessageTitle</h4>
}
</button>
}
</td>
</tr>
}
</table>
</div>
<div class="right" id="showMessage">
#Html.Partial("ReadMessage", new BlocketProject.Models.DbClasses.DbMessages())
</div>
Razor is run on the server, before the page is rendered. Once the page is on the client, and they can click a message, the concept of the PartialView is gone - it's all just one HTML page.
The easiest way for you to do this is to use an Ajax.BeginForm where your button is, and on click, update an element with a partial view retrieved from the server. Something like:
#using(Ajax.BeginForm("ReadMessage", "Messages", new AjaxOptions() { UpdateTargetId = "showMessage" })) {
//...
}
//...
<div class="right" id="showMessage">
//ReadMessage partial rendered on button click
</div>
A little method that could help you :
protected ActionResult View(string viewName, object model)
{
if (ControllerContext.IsChildAction)
return PartialView(viewName, model);
else if (Request.IsAjaxRequest())
return PartialView(viewName, model);
else
return View(viewName, model);
}
This will return a PartialView when you call you action via #Html.RenderAction() or call the action via Ajax (jQuery).
You can then use jQuery to prevent the form to be posted and post it with Ajax or when you click on a message, you can also use jQuery to get the result from the action and insert it in your DOM.
I'm just wrapping up a college project, I'm not sure if I've been staring at my computer for too long and am missing something obvious, but when I try to log a user out, I'm getting a 404 not found for the URL /Account/LogOff.
I have a navbar that shows Log in/Log out depending on whether a user is, logged in, or, logged out:
<div class="nav-collapse collapse">
<ul class="nav pull-right">
<li class="dropdown" id="dropdown-login-div">
#if (!Request.IsAuthenticated)
{
<a class="dropdown-toggle" href="#" data-toggle="dropdown">Sign In <strong class="caret"></strong></a>
}
else
{
#Html.ActionLink("Log Off", "LogOff", "Account")
}
<div class="dropdown-menu" id="dropdown-login">
#Html.Partial("~/Views/Account/_LoginPartial.cshtml", new ViewDataDictionary<LetLord.Models.LoginModel>())
</div>
</li>
</ul>
</div>
In my Account controller the default LogOff action that comes with the Internet template:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
WebSecurity.Logout();
return View("Index");
}
Could someone tell me why this happening - before I throw my laptop against the wall. Cheers.
You use a link (<a/> tag) to log off which results in HTTP GET request when user clicks on it, but your action is constrained to serve POST request only (because it is decorated with [HttpPost] attribute).
You either need to put your link into a form and generate POST request, or remove [HttpPost] and [ValidateAntiForgeryToken] (credits to GalacticCowboy) from your action.
Since logout modifies server state, I wouldnt remove [HttpPost] and [ValidateAntiForgeryToken]
Instead I will replace the link (anchor tag) with the following
#using (Html.BeginForm("Log Out", "Account", FormMethod.Post,
new { id = "logoutForm" }))
{
#Html.AntiForgeryToken()
Log Out
}
I ran into this issue on a legacy app. The way that I fixed it was to detect when the supplied return Url was '/Account/LogOff' and act accordingly.
From the 'AccountController.cs' file, 'Login' method:
if (returnUrl == "/Account/LogOff")
{
return this.RedirectToLocal(null);
}
else
{
return this.RedirectToLocal(returnUrl);
}
The Scenario is this...I goto the login screen on my web app. I login with wrong username/password. I get an error on the line in my HTML for tab-6 #HTML.Partial("../../Account/Register") because the RegisterModel is not passed in(Exact error below). Well I'm not doing anything with the Register screen, just logging in.
However, I think the problem is because the Register section is always loaded. So I'm thinking, I don't need to pass in both LogOnModel and RegisterModel, to fix the problem, but maybe should not load the Register page until it is specifically called, I think? Is that the correct thing to do and how would I load the Register page only when it is called/clicked. I'm using JQuery UI tabs and asp.net mvc.
ERROR:
The model item passed into the dictionary is of type 'Portal.Web.Models.LogOnModel', but this dictionary requires a model item of type 'Portal.Web.Models.RegisterModel'.
Thanks a lot!
<div id="tabs" class="ui-tabs ui-widget ui-widget-content ui-corner-all" style=" position:relative; border:0px;" >
<ul class="ui-tabs-nav">
<li><a href="#tabs-1" >Home</a></li>
<li><a href="#tabs-2" >Statistics</a></li>
<li><a href="#tabs-3" >Topo Maps</a></li>
<li><a href="#tabs-4" >FAQs</a></li>
<li style="display:none;">Login</li>
<li style="display:none;">SignUp</li>
</ul>
<div id="tabs-1" class="ui-tabs-hide ui-tabs-panel">#Html.Partial("../Home/Home") </div>
<div id="tabs-2" class="ui-tabs-hide ui-tabs-panel">#Html.Partial("../Statistics/Statistics")</div>
<div id="tabs-3" class="ui-tabs-hide ui-tabs-panel">#Html.Partial("../Maps/Maps")</div>
<div id="tabs-4" class="ui-tabs-hide ui-tabs-panel">#Html.Partial("../Home/FAQs")</div>
<div id="tabs-5" class="ui-tabs-hide ui-tabs-panel">#Html.Partial("../Account/LogOn")</div>
<div id="tabs-6" class="ui-tabs-hide ui-tabs-panel">#Html.Partial("../Account/Register")</div>
</div>
Here is my AccountController with action method
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
return View(model);
}
As Requested Here is the additional code:
public ActionResult LogOn()
{
return View();
}
Double-check that you've set your partial view to inherit from the appropriate model. At the top of your partial view code, you should have a line like this.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Portal.Web.Models.LogOnModel>" %>