How do I pass data between controllers and overloaded actions? - asp.net

I have one controller that takes a username and pass and checks against a database. IF the user is authenticated, I want to call an overloaded action on another controller.
My end goal is to authenticate a user against an old table from a MySQL db (I have this part working). Once the user is authenticated, I would like to be able to "automagically" forward the person to the built in MVC registration page but I would like to populate some fields in the view using data obtained from the first controller (the old databse info).
When I try something like what I have below I get an error about the Register() methods being ambiguous. I've also tried using the [ActionName("Register2")] attribute but then the error returned says it cant find a method named Register2.
public class MigrateAccountController : Controller
{
OldUserRepository oldDb = new OldUserRepository();
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(User u)
{
if (oldDb.isValid(u.username, u.password))
return RedirectToAction("Register", "Account", u);
return View(u);
}
}
public class AccountController : Controller
{
public IFormsAuthenticationService FormsService { get; set; }
public IMembershipService MembershipService { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if (FormsService == null) { FormsService = new FormsAuthenticationService(); }
if (MembershipService == null) { MembershipService = new AccountMembershipService(); }
base.Initialize(requestContext);
}
public ActionResult Register(User u)
{
return View(u);
}
public ActionResult Register()
{
ViewBag.PasswordLength = MembershipService.MinPasswordLength;
return View();
}
}

First thing you cannot have the same action name on the same controller that is accessible on the same verb. You need to either change the action name or use a different HTTP verb:
public class AccountController : Controller
{
public IFormsAuthenticationService FormsService { get; set; }
public IMembershipService MembershipService { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if (FormsService == null) { FormsService = new FormsAuthenticationService(); }
if (MembershipService == null) { MembershipService = new AccountMembershipService(); }
base.Initialize(requestContext);
}
[HttpPost]
public ActionResult Register(User u)
{
return View(u);
}
public ActionResult Register()
{
ViewBag.PasswordLength = MembershipService.MinPasswordLength;
return View();
}
}
and in order to pass data between actions, well, if you are using GET, you could pass them as query string parameters when redirecting.
Or IMHO a better way would be not to redirect in this case but simply return the corresponding view by passing it the proper view model:
[HttpPost]
public ActionResult Index(User u)
{
if (oldDb.isValid(u.username, u.password))
{
return View("~/Account/Register.aspx", u);
}
return View(u);
}

You can use the TempData values in this case.

Related

Asp.Net WebApi - Multiple actions were found that match the request

With the routeTemplate defined as "api/{controller}/{id}" and having below methods in my controller (ValuesController)
[HttpGet]
public IEnumerable<string> GetX()
{
return new string[] { "xx1", "xx2" };
}
[HttpGet]
public IEnumerable<string> GetY([FromUri]Customer c)
{
return new string[] { "c1", "c2" };
}
public class Customer
{
public bool IsMarried { get; set; }
public string CustName { get; set; }
}
Using the url - "/api/values?IsMarried=false&CustName=aa" results in error - "Multiple actions were found that match the request..." .
I am not able to fix above issue without changing the routeTemplate to something like "api/{controller}/{action}/{id}".
If anyone knows how to fix above without changing the routeTemplate kindly suggest.
Solution one: Routing
[RoutePrefix("api")]
public class FooController : ApiController
{
[Route("Foo/getall")]
[HttpGet]
public IEnumerable<string> GetX()
{
return new string[] { "xx1", "xx2" };
}
[Route("foo/search")]
[HttpGet]
public IEnumerable<string> GetY([FromUri]Customer c)
{
return new string[] { "c1", "c2" };
}
public class Customer
{
public bool IsMarried { get; set; }
public string CustName { get; set; }
}
}
Remember to add attribute route mapping:
configuration.MapHttpAttributeRoutes();
Solution two: Optional parameters
[RoutePrefix("api")]
public class FooController : ApiController
{
[HttpGet]
public IEnumerable<string> GetY([FromUri]Customer c)
{
if(c == null)
// no parameters where set.
if(c.IsMarried != null)
// do stuff
// etc....
return new string[] { "c1", "c2" };
}
public class Customer
{
public bool? IsMarried { get; set; }
public string CustName { get; set; }
}
}
If you cant specify action names then you have two options.
Action based on Parameter
Create an overload that takes a default (null for reference type) parameter. Depending on the state o the parameter do the approriate action. As your sample is very abstract I have no idea if this would work for you or not.
[HttpGet]
public IEnumerable<string> GetY([FromUri]Customer c = default(Customer))
{
if(c == null /* or some other state */)
return new string[] { "xx1", "xx2" };
return new string[] { "c1", "c2" };
}
New Controller
Create a new Controller and define your additional action there. If the actions are very different in nature this might be a better solution. No code sample here is needed, just create a new controller and move the existing method.
Use simple types and map them in the controller:
[HttpGet]
public IEnumerable<string> GetY(bool IsMarried, string CustName)
{
var cust = new Customer {IsMarried = IsMarried, CustName = CustName};
return new string[] { "c1", "c2" };
}
Note that you will lose functionalities like ModelState.IsValid so you would need to invoke the validators manually.
Take a look at Action Selection section to see how it works for simple vs complex params.

Manually validate Model in Web api controller

I have a class called 'User' and a property 'Name'
public class User
{
[Required]
public string Name { get; set; }
}
And api controller method is
public IHttpActionResult PostUser()
{
User u = new User();
u.Name = null;
if (!ModelState.IsValid)
return BadRequest(ModelState);
return Ok(u);
}
How do i manually validate the User object so the ModelState.IsValid return false to me?
You can use the Validate() method of the ApiController class to manually validate the model and set the ModelState.
public IHttpActionResult PostUser()
{
User u = new User();
u.Name = null;
this.Validate(u);
if (!ModelState.IsValid)
return BadRequest(ModelState);
return Ok(u);
}
This answer is not for this case, but it is very relevant if you want to validate a parameter manually:
public IHttpActionResult Post(User user)
{
ModelState.Clear(); // remove validation of 'user'
// validation is done automatically when the action
// starts the execution
// apply some modifications ...
Validate(user); // it adds new keys to 'ModelState', it does not update any keys
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// ...
}
You will need to define custom Validation Attribute as
class CustomValidatorAttribute : ValidationAttribute
{
//custom message in ctor
public CustomValidator() : base("My custom message") {}
public CustomValidator(string Message) : base(Message) {}
public override bool IsValid(object value)
{
return !string.IsNullOrWhiteSpace(value.ToString());
}
//return a overriden ValidationResult
protected override ValidationResult IsValid(Object value,ValidationContext validationContext)
{
if (IsValid(value)) return ValidationResult.Success;
var message = "ohoh";
return new ValidationResult(message);
}
}
likewise in your model class
public class User
{
[Required]
[CustomValidator("error")]
public string Name { get; set; }
}
The model should be an input parameter to your ActionMethod, and ModelState.IsValid will validate as per the attributes you set in the Model class, in this case as it is set [Required] it will be validated againg null values,
and if you just wish to manually check whether there is a value, you can check it directly.
if (user.Name == null) {
return;
}

How to keep the values filled in a form and recover after navigating to another controller

I am facing this situation. I have a form that I filled with three values. Then by clicking on a button I'm in another controller and I create an XmlDocument object I recovered via TempData. Once completed action coming back to my form naturally when all data has disappeared. My question is, how do I fill out the form, keep these values, and once the XML created to fill my database with the form data and XML created.
Layout controller
public class LayoutController : Controller
{
[HttpGet]
public ActionResult Create()
{
var Layout = new LayoutModel();
return View(Layout);
}
[HttpPost]
public ActionResult Create(LayoutModel Layout)
{
if (Layout == null)
{
return Content("le LayoutModel est nul");
}
else
{
TempData["DocName"] = Layout.Nom;
if (TempData["xmlAssociated"] != null)
{
Layout.xmlAssociated = (string)TempData["xmlAssociated"];
ManageXML.Models.COracleConn.GetInstance().InsertLayout(Layout);
}
else
{
return Content("On a pas recuperé l'XML");
}
return RedirectToAction("ListOfTopsAndMarges", "Entete_Marge");
}
}
}
XMLRecord controller
public class XMLRecordController : Controller
{
[HttpGet]
public ActionResult HandleForm()
{
var file = new XMLRecord()
{
Records = new List<XMLRecord>(){ new XMLRecord(){ Type="", Contenu="" }}
};
return View(file);
}
[HttpPost]
public ActionResult HandleForm(XMLRecord file)
{
if (file == null)
{
return HttpNotFound();
}
else
{
file.DocName = (string)TempData["DocName"];
string recup = file.GenerateXML(file);
TempData["xmlAssociated"] = recup;
return RedirectToAction("Create", "Layout");
}
}
}
public class LayoutModel
{
public string Nom { get; set; }
public string Type { get; set; }
public string Contenu { get; set; }
public string xmlAssociated {get; set;}
}
I want to retain my Type, my Contenu and my Nom properties even if I am going to another controller
Thnak for your help #Heymega

How to add the UserId to posted data by Logged User in ASP.NET MVC 4

So what I'm doing might seem simple, but I don't know exactly how to do it.
I have already registered and logged in with an account (I'm using the default membership system used in ASP.NET MVC 4) and so I want to do add my UserId to some data I'm inserting to the database.
This is the model of the data I'm inserting:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Reroute.Models
{
public class Request
{
public int RequestId { get; set; }
// I want to add UserId based on my current session
public int UserId { get; set; }
public string OrderNumber { get; set; }
public string TrackingNumber { get; set; }
public string CurrentAddress { get; set; }
public string NewAddress { get; set; }
public string Comment { get; set; }
}
}
And the ActionResult (here's where I supposed I have to make the changes):
[HttpPost]
public ActionResult Create(Request collection)
{
try
{
_db.Requests.Add(collection);
_db.SaveChanges();
//return RedirectToAction("Index");
return Content("Done! Added to DB");
}
catch
{
return View();
}
}
Thanks
use this it gets u the userid ...
Membership.GetUser().ProviderUserKey
You can save the UserId of the authenticated user in Session after logging in:
Session["UserId"] = userId;
or since you are using FormsAuthentication you can either use the UserData property as shown here or do a nice-that-will-do-trick:
public SignInUser(string name, string id) {
// store the userid
FormsAuthentication.SetAuthCookie(name + '|' + id, false);
}
then retrieve the Name and UserId like this:
public int CurrentUserId
{
get
{
var context = HttpContext.Current;
if (context == null) return 0;
return context.Request.IsAuthenticated
? Convert.ToInt32(context.User.Identity.Name.Split('|')[1])
: 0;
}
}
public string CurrentUserName
{
get
{
var context = HttpContext.Current;
if (context == null) return string.Empty;
return context.Request.IsAuthenticated
? context.User.Identity.Name.Split('|')[0]
: string.Empty;
}
}
You can have those method and properties in a class so you have them in one place, I actually do it that way. Now, you can call it in your controller like so:
[HttpPost]
public ActionResult Create(Request collection)
{
try
{
collection.UserId = _authProvider.CurrentUserId;
// if you want to use session, I prefer the FormsAuthentication approach
// you need to do additional check that the Session has not expired (not null)
collection.UserId = Session["UserId"];
_db.Requests.Add(collection);
_db.SaveChanges();
//return RedirectToAction("Index");
return Content("Done! Added to DB");
}
catch
{
return View();
}
}
_authProvider is an instance of the class that has the code I gave above.
This should work.
var loggedInUserName=Thread.CurrentPrincipal.Identity.Name;
var user=Membership.GetUser(loggedInUserName);
var key = user.ProviderUserKey;
T
Assuming your Create also has a GET which is loaded up and used as the model for Create.cshtml, you would just need to set it explicitly in that ActionResult
public ActionResult Create()
{
Result model = new Result();
model.UserId = myUserId;
}
Then in your Create.cshtml you could have a hidden field for it:
#Html.HiddenFor(m => m.UserId)
I would still check in the POST to make sure the user doing the saving is allowed to be saving and hasn't spoofed your hidden field value to somebody completely different.

Many to many not saving when updating/editing entity

I'm having a problem with my [HttpPost] edit method in my controller, it is not saving the changes made to a userrole, it is strange because the create method is working it is using the same helper methods, this is my code:
viewmodel:
public class UserViewModel
{
public User User { get; set; }
public virtual ICollection<AssignedUserRole> UserRoles { get; set; }
public virtual List<Company> Companies { get; set; }
}
Controller:
[HttpPost]
public ActionResult Edit(UserViewModel userViewModel)
{
if (ModelState.IsValid)
{
var user = userViewModel.User;
user.UserRoles.Clear();
AddOrUpdateRoles(user, userViewModel.UserRoles);
context.Entry(user).State = EntityState.Modified;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(userViewModel);
}
Helper Method
private void AddOrUpdateRoles(User user, ICollection<AssignedUserRole> assignedUserRoles)
{
foreach (var assignedRole in assignedUserRoles)
{
if (assignedRole.Assigned)
{
var userRole = new UserRole { Id = assignedRole.UserRoleId };
context.UserRoles.Attach(userRole);
user.UserRoles.Add(userRole);
}
}
}
everything in the User object is being updated except for the userrole, I can't find the problem as I am debugging and doing a step through and I can see that the user has the correct/updated roles assigned.
I was able to solve this problem by making the following changes:
private void AddOrUpdateRoles(User user, ICollection<AssignedUserRole> assignedUserRoles)
{
foreach (var assignedRole in assignedUserRoles)
{
if (assignedRole.Assigned)
{
var userRole = context.UserRoles.Find(assignedRole.UserRoleId);
user.UserRoles.Add(userRole);
}
}
}
[HttpPost]
public ActionResult Edit(UserViewModel userViewModel)
{
if (ModelState.IsValid)
{
var user = userViewModel.User;
context.Entry(user).State = EntityState.Modified;
context.Entry(user).Collection(u => u.UserRoles).Load();
user.UserRoles.Clear();
AddOrUpdateRoles(user, userViewModel.UserRoles);
context.SaveChanges();
return RedirectToAction("Index");
}
return View(userViewModel);
}
I had to "Load" the user's userroles otherwise the clearing was doing nothing.

Resources