Action not being hit from another controller - c#

I am asking a simple login page to get the user to enter a pin on a mobile responsive app its in house not online.
When the page is posted the user clicks the submit button the SaveUsers is the function that is called first this is on the Login Controller.
[HttpPost]
[ActionName("Index")]
public ActionResult SaveUsers(Users model)
{
BrianScott_SOMEntities usr = new BrianScott_SOMEntities();
var s = usr.GetUsers(model.Pin);
var item = s.FirstOrDefault();
if (item == "Success")
{
var sageDetails = usr.Users.Where(w => w.Pin ==model.Pin).FirstOrDefault();
HttpCookie cookie = new HttpCookie("BScotSalesOrderManagerLogin");
cookie.Values.Add("SageUserName", sageDetails.SageUserName.ToString());
cookie.Values.Add("SagePassword", sageDetails.SagePassWord.ToString());
cookie.Expires = DateTime.Now.AddDays(30);
Response.Cookies.Add(cookie);
return View("~/Views/Home/Index.cshtml");
}
else if (item == "User Does not Exists")
{
ViewBag.NotValidUser = item;
}
else
{
ViewBag.Failedcount = item;
}
return View("Index.cshtml");
}
The below form is the form that is represented with the above controller which prompts the user for their pin number.
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#model Web.SOM.Models.Users
#using (Html.BeginForm())
{
<div class="bs-example" style="border:2px solid gray;">
<label>#ViewBag.SageUserName</label>
<div class="form-group centerlook">
<h1> Login </h1>
</div>
<div class="form-group centerlook">
<label>Pin: </label>
#Html.EditorFor(model => model.Pin )*
#Html.ValidationMessageFor(model => model.Pin)
</div>
<div class="form-group error">
#if (#ViewBag.Failedcount != null)
{
<label> Failed Attempt count is: #ViewBag.Failedcount</label>
}
#if (#ViewBag.NotValidUser != null)
{
<label> #ViewBag.NotValidUser</label>
}
</div>
<div class="loginbtn">
<input type="submit" value="Login" class="btn btn-primary" />
</div>
</div>
}
The home controller is what the controller in the first piece of codes redirects to it is the index action to which I want to hit.
public ActionResult Index()
{
if (Request.Cookies["BScotSalesOrderManagerLogin"] != null)
{
ViewBag.SageUserName = Request.Cookies["BScotSalesOrderManagerLogin"].Values["SageUserName"];
ViewBag.SagePassword = Request.Cookies["BScotSalesOrderManagerLogin"].Values["SagePassword"];
}
return View();
}
But the method is not getting hit unless I do a hard reload is there another way of moving to the other view and making sure that the index method is being hit ?.
Because when I look at the viewbag the items are null when they should contain a username and a password of the related row when I debug on the first page the values are their but then lost on the other page.
On my page i am wanting to display the information I am just doing
<label>Sage Username : #ViewBag.SageUserName </label>
But the value is blank? I am coming from a web forms background so please excuse me better late than never jumping ship

Your sequence of events is...
User POSTs to SaveUsers on Login (aliased as the action Index)
You return a view
User POSTs from that view to Index, still on Login
When you do this:
return View("~/Views/Home/Index.cshtml");
You're essentially overriding the framework's standard behavior. You're returning a specific file to be used as the view, but not telling the framework to switch any context or change the URL in any way. So from the browser's perspective, even though you returned a view from your server-side Home folder, the page is still /Login/Index and any form actions, links, etc. will be from there.
In general you should prefer redirects to manually specifying views. So a sensible sequence of events might be:
User POSTs to SaveUsers on Login (aliased as the action Index)
Server-side code performs its logic, redirects the user to Index on Home
User GETs Index on Home
Server returns the view
User POSTs to Index on Home
So your SaveUsers action method, when successful, can do something like:
return RedirectToAction("Index", "Home");
This will then cause the user to make a GET request to Index on Home, which can just return View() and that will default to the view you're manually returning now. Now from the browser's perspective it's on /Home/Index and all form actions, links, etc. will be from that context.
Additionally, if you always want a given form, link, etc. to point to a specific controller action regardless of where it was loaded from, you can specify that. For example, when you do this:
using (Html.BeginForm())
You are telling the framework that this form will "POST to the current URL, whatever that URL is". But when you do this:
using (Html.BeginForm("Index", "Home"))
This tells the framework that the form will always post to the Index action on the Home controller, regardless of the current URL.

can you try using
#using (Html.BeginForm(yourActionName, yourControllerName))
{
}
And use RedirectToAction to return to your Home Index..

Related

How to pass a model and a variable to a post method with one input

I'm trying to have one post route that takes care of multiple operations.
Here's the Controller:
[HttpPost("dish/{operation}")]
public IActionResult Dish(string operation, Dish dish)
{
if (operation == "RedEdit")
{
return RedirectToAction("EditDish", dish);
}
if (ModelState.IsValid)
{
if (operation == "Add")
{
_context.Add(dish);
_context.SaveChanges();
return RedirectToAction("Index");
}
else //Unused currently
{
Console.WriteLine("Oops");
_context.Add(dish);
_context.SaveChanges();
return RedirectToAction("Index");
}
}
else
{
return View("DishForm");
}
}
The POST route will take a string, which is the operation it'll do, and depending on the operation it'll run something different. Right now I don't have all the operations, and my else within the validation isn't what it's going to be. The problem I'm having currently is with the "RedEdit," which is just a method to redirect to the edit page. Here's my view and what I'd like to do:
#{
ViewData["Title"] = "Add a dish!";
ViewData["Header"] = "Add a new Dish!";
ViewData["Home"] = true;
ViewData["Add"] = false;
var parms = new Dictionary<string, string>
{
{"operation", ""}
};
}
#model RichCRUDelicious.Models.Dish
<div class="container d-flex flex-column text-center">
<h3><u>#Model.Name by #Model.Chef</u></h3>
<p>#Model.Description</p>
<p>Calories: #Model.Calories</p>
<p>Tastiness: #Model.Tastiness</p>
<footer>
#using (Html.BeginForm("Dish", "Home", "operation", FormMethod.Post)){
//Two Buttons with Edit and Delete
}
</footer>
</div>
I'd essentially like to have one form, which has two buttons, one for edit and delete. The button for edit will change my operation value in parms to "RedEdit," while delete will change it to "Delete" (which I don't have a route set up for currently but that's not the issue.) I've tried a couple different methods, and mostly the issue comes down to the parameters within the post method, I'm not sure how I can pass the model in AND the operation value. I don't mind if they're split up into two different forms, but I'd really like just one post method for this controller.
I've tried using a generic HTML Form with:
<form asp-action="Dish" asp-controller="Home" asp-all-route-data="parms" method="post">
But my issue wasn't resolved using this method either, I'm thinking a hidden input with two different forms will work, but if there's a better way I'd like to hear.
If you want to use a form with two buttons which go to the same action,you can try to add asp-route-operation to your buttons,here is a simple demo:
Dish:
public class Dish {
public int Id { get; set; }
public string Name { get; set; }
}
view:
<form method="post">
<input hidden name="Id" value="1" />
<input hidden name="Name" value="test" />
<button asp-controller="Home" asp-action="Dish" asp-route-operation="RedEdit">RedEdit</button>
<button asp-controller="Home" asp-action="Dish" asp-route-operation="Delete">Delete</button>
</form>
action:
[HttpPost("dish/{operation}")]
public IActionResult Dish(string operation, Dish dish)
{
...
}
}
Hidden inputs will help you pass the value of Dish to the action,asp-route-operation will help pass different operation values to the action.When clicking RedEdit button,the value of operation will be RedEdit.With Delete button,it will be Delete.

How can i send back a model containing two lists from the view to my controller

I have a cshtml view, for which I am sending a viewmodel. That viewmodel consist of two list of albums (music albums). I then ask the user to check up to 3 of these albums (a checkbox next to the title) to vote for his favorite music. I use javascript to ensure he doesn't check anymore than 3 (The security is a detail right now, I'm more concerned about getting it to work, but I'm open to suggestion if people have a better solution).
Since all albums are displayed in a table, I would love to send back to the controller through the submit button, the same model after updating it.
Basically, one of the list contain the current vote the user has made before loading the page (can be empty), and the second one should be empty until sent back to the controller containing the list of votes that are currently selected. I then use these two lists to compare them and update the database, removing the votes he removed, and adding the vote he added.
But I am unable to create a proper form to return these informations as I am not used to forms.
I tried to put the whole list in a form, but it didn't work. My reserach when I look for "sending model back to controller" usually do just that and get it to work.
View model
public class CategoryVotesUserViewModels
{
public CategoryVoteViewModels categoryVoteViewModels;
public List<int> listVotesEntry = new List<int>();
public List<int> listVotesOutput = new List<int>();
}
Relevant CSHTML and javascript
#section Header{
<script>
var MAX_VOTES = 3;
function checkNumberVotes($this) {
console.log($("input[name='listVoteOutput']:checked"));
if ($("input[name='listVoteOutput']:checked").length > MAX_VOTES) {
$this.checked = false;
}
}
</script>
}
#using (Html.BeginForm("VoteInCategory", "Votes", new { SearchModel = Model }, FormMethod.Post))
{
<ul>
#foreach (var av in Model.categoryVoteViewModels.listVotes)
{
<li>
#av.album.Title | #av.votes |
<input type="checkbox"
name="listVoteOutput"
value=#av.album.ID
onclick="checkNumberVotes(this)"
#if (Model.listVotesEntry.Contains(av.album.ID))
{ <text> checked </text> } />
</li>
}
</ul>
<div class="form-group text-center">
<input type="submit" class="btn btn-primary" value="Submit" />
</div>
}
Controller
[HttpPost]
public ActionResult VoteInCategory(CategoryVotesUserViewModels categoryVotesUserViewModels)
{
if (ModelState.IsValid)
{
List<int> toAdd = categoryVotesUserViewModels.listVotesOutput.Except(categoryVotesUserViewModels.listVotesEntry).ToList();
List<int> toRemove = categoryVotesUserViewModels.listVotesEntry.Except(categoryVotesUserViewModels.listVotesOutput).ToList();
VoteService.updateVoteUserCategory(User.Identity.GetUserId(), toRemove, toAdd, categoryVotesUserViewModels.categoryVoteViewModels.categoryID);
//TODO Redirect to success
return RedirectToAction("Index", "Home");
}
return View(categoryVotesUserViewModels);
}
If the user already had voted, all album whose ID is in "ListVotesEntry" should begin checked. If the user hasn't voted, or voted for nothing previously, "ListVotesEntry" should be empty.
When the User press the submit button, if an album is checked, the album's id should be added to the "ListVotesOutput" list. Also, both "ListVotesEntry" and "ListVotesOutput" should be sent back to the controller. The list with the names of the albums and their titles/ID is no longer necessary for the rest of the treatment
Found the solution. The problem was that my model needed to use "{get; set;}" on its attributes, otherwise the binding doesn't work, which mean that it send back an empty model.

login in partial view is not working

I am trying to create a login on a partial view without the need to refresh the whole browser when someone logs in.
The login controller works perfectly and also it selects ok the partial View that it has to be shown later. The problem is that this partial View doesn't detect that the user is logged.
If I refresh the web (F5) I can see the user is, as I said, well logged.
Controler:
[AllowAnonymous]
public ActionResult Login() {
return PartialView("_Login");
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl) {
if(ModelState.IsValid) {
if(WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe.Value)) {
return PartialView("LogOnPartialView", "Shared");
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
return View(model);
}
Partial View _login
#using (Ajax.BeginForm("Login", "Account", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "loginControl" }))
{
#Html.AntiForgeryToken()
<div class="form-field">
#Html.EditorFor(m => m.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</div>
<div class="form-field">
#Html.EditorFor(m => m.Password)
#Html.ValidationMessageFor(m => m.Password)
</div>
<input type="submit" value="Login" />
}
Partial View LogOnPartialView
#if (HttpContext.Current.User.Identity.IsAuthenticated)
{
<div>user is logged</div>
}
else
{
<div>user is NOT logged</div>
}
#if(!Request.IsAuthenticated)
{
#Html.ActionLink("Registro", "Register", "Account")
#: |
#Ajax.ActionLink("Login", "Login", "Account", new AjaxOptions { UpdateTargetId = "Login" })
}
else
{
#: Welcome <b>#User.Identity.Name</b>!
#: |
#Html.ActionLink("Logout", "LogOff", "Account")
}
Could anyone help me with this, please?
I doubt you are going to get this working in its current form. If you read the docs it states
When a user is logged in, ASP.NET sets an authentication token in a cookie that lets ASP.NET know on subsequent requests that the user has been logged in.
Notice the use of the term 'subsequent requests'.
The problem with using it in the same request is the incoming request has not sent the auth cookie and thus IsAuthenticated is false.
i.e.
Browser submits Login
Server responds with Set-Cookie
Browser sends this cookie (the auth cookie) on the next request
This is the reason it works when you hit F5
What you can do to resolve this is take the result of the call to WebSecurity.Login which indicates whether the user logged in successfully and pass it to your LogOnPartialView

Call ActionResult method from button press

I'm trying to call an ActionResult method from a web page form but I can't seem get the two items to connect.
The desired result is for the page to refresh and filter the model to display the required results.
Method in serversController.cs
The internal code works as intended when placed in ActionResult Index
[HttpPost]
public ActionResult activeServers()
{
// load the servers into local variable
var servers = from s in db.Servers
select s;
// filter out the archived servers
servers = servers.Where(s => s.archive.Equals(0));
return View(servers.ToList());
}
Button making the call
#using (Html.BeginForm())
{
<button name="activeServers" type="submit" value="activeServers" class="btn btn-default">Active</button>
}
Thanks is advance
Try to specify the action method, controller name (without the controller suffix) and the http method (it defaults to GET) in the BeginForm:
#using (Html.BeginForm("activeServers", "Servers", FormMethod.POST))
{
<button name="activeServers" type="submit" value="activeServers" class="btn btn-default">Active</button>
}

how to redirect user to another page and then back, while persisting form input values

I have an exercise edit form with various input fields. This form posts to a Save controller action after the user fills out the exercise details and finally clicks the save submit button. Within this form is a link (right now an ActionLink) that redirects the user to another page to choose an exercise from a catalog (this is helpful so the user can choose the exercise from a nice catalog with images instead of just a drop down of exercise names on the exercise edit form). After the user chooses an exercise from the catalog then they are redirected back to the exercise edit page, where I pass the exerciseId that was chosen from the catalog page. The exercise name drop down is then set to the exercise that was chosen from the catalog page. The user can then save the exercise details.
The problem I'm having is that I can't figure out a good way to persist the user's input field values after the redirect to the exercise catalog page. When the user finally gets redirected back to the exercise edit page then the exercise details that the user had filled out prior to the redirect are gone (which makes sense, because I'm not correctly persisting them currently.
My current method for trying to persist the user's input values is to use an actionlink to send the exercise model values as anonymous object parameters to the redirect controller action, however the model property values are null or 0 when passed. It seems like the actionlink doesn't pass the input values that the user entered.
I thought I could use jQuery to take care of the redirect stuff, so I tried doing a jQuery .post, however I couldn't immediately redirect to another controller action once on the server, I'd have to go into the .post callback function and then set window url to the redirect controller action. However, this seems inefficient with the multiple trips to the server.
I feel like a solution would be to just pass these input values in a form submission, however I'd then have a form within a form, which I tried and couldn't get to work.
#using (Html.BeginForm("Edit", "WorkoutPlanExercise",
FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="editExercise">
#Html.ValidationSummary(true)
#Html.HiddenFor(m => Model.WorkoutPlanExerciseId)
#Html.HiddenFor(m => Model.WorkoutPlanId)
#Html.HiddenFor(m => Model.ImageMimeType)
<div class="editRow">
<div class="editExerciselbl">
#Html.LabelFor(m => Model.Name):
</div>
<div class="editExerciseInput">
#Html.DropDownListFor(
x => x.SelectedExerciseId,
Model.AllExercises,
null,
new { #id = "ddlExercise" }
)
</div>
<span>
#Html.ActionLink("Browser Exercise Catalog", "ChooseCatalogExercise",
new {
workoutPlanExerciseId = Model.WorkoutPlanExerciseId,
workoutPlanId = Model.WorkoutPlanId,
reps = Model.Reps,
sets = Model.Sets,
weight = Model.Weight
})
</span>
</div>
<div class="editRow">
<div class="editExerciselbl">
#Html.LabelFor(m => Model.Reps):
</div>
<div class="editExerciseInput">
#Html.EditorFor(m => Model.Reps)
#Html.ValidationMessageFor(m => m.Reps)
</div>
</div>
<div class="editRow">
<div class="editExerciselbl">
#Html.LabelFor(m => Model.Weight):
</div>
<div class="editExerciseInput">
#Html.EditorFor(m => Model.Weight)
#Html.ValidationMessageFor(m => m.Weight)
</div>
</div>
<div class="editRow">
<div class="editExerciselbl">
#Html.LabelFor(m => Model.Sets):
</div>
<div class="editExerciseInput">
#Html.EditorFor(m => Model.Sets, new { #Value = "0" })
#Html.ValidationMessageFor(m => m.Sets)
</div>
</div>
<div id="exerciseImageContainer">
#Html.Partial("Edit.ExerciseImage", Model)
</div>
<input id="save" type="submit" value="Save" />
#Html.ActionLink("Cancel and return to List", "Index", new { workoutPlanId = Model.WorkoutPlanId })
</div>
}
controller:
public ActionResult ChooseCatalogExercise(int workoutPlanExerciseId, int workoutPlanId, int reps, int sets, decimal weight)
{
return RedirectToAction("ChooseCatalogExercise", "ExerciseCatalog",
new { workoutPlanExerciseId = model.WorkoutPlanExerciseId, workoutPlanId = model.WorkoutPlanId, reps = model.Reps, sets = model.Sets, weight = model.Weight });
}
Some possibilities I can think of:
Use session state on the server to maintain and repopulate the values when rendering the page back out when returning to the catalog page. (Do not do this if you have to scale)
Use a cookie to hold the state of the fields and repopulate them when returning (probably not going to work as you will hit the cookie length limit)
Instead of redirecting to another page, use a jquery ui dialog, bootstrap modal, or some other pop-over to select the exercise
I think the 3rd is the best. It makes your site more responsive anyway.
"It seems like the actionlink doesn't pass the input values that the
user entered."
You need a form submit in order to pass the values to server side. Therefore, action link doesn't submit the form values.
In your scenario I think you may need to change the view structure. Something like, avoiding navigating to another view in between. You may use a Jquery solution similar to following.
http://www.ericmmartin.com/projects/simplemodal-demos/

Categories

Resources