Currently I'm using a standard Manage page which looks like:
public ActionResult Manage(string type = "")
{
ViewBag.Type = type;
switch (type)
{
case "EmailAddress":
ViewBag.EmailAddress = lol.UserProfiles.Find((int)Membership.GetUser().ProviderUserKey).EmailAddress;
break;
case "Password":
break;
default:
break;
}
//ViewBag.ReturnUrl = Url.Action("Manage/" + type);
return View();
}
Now my Manage Model page looks like:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Manage(ManageViewModel model)
{
if (ModelState.IsValid)
{
if (model.EmailAddressModel != null)
{
try
{
int userID = (int)Membership.GetUser().ProviderUserKey;
var User = lol.UserProfiles.First(f => f.UserId == userID);
User.EmailAddress = model.EmailAddressModel.EmailAddress;
int saveStatus = lol.SaveChanges();
if (saveStatus == 1)
{
ViewBag.StatusMessage = MessagesEnum.ChangeEmailSuccess;
return RedirectToAction("Manage/EmailAddress", "Account");
}
else
{
ModelState.AddModelError("", MessagesEnum.ChangeEmailFailed);
}
}
catch { }
}
else
{
// ChangePassword will throw an exception rather than return false in certain failure scenarios.
bool changePasswordSucceeded;
try
{
changePasswordSucceeded = WebSecurity.ChangePassword(User.Identity.Name, model.PasswordModel.OldPassword, model.PasswordModel.NewPassword);
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
ViewBag.StatusMessage = MessagesEnum.ChangePasswordSuccess;
return RedirectToAction("Manage/Password", "Account");
}
else
{
ModelState.AddModelError("", MessagesEnum.ChangePasswordFailed);
}
}
}
else
{
}
return View(model);
}
My ManageViewModel class:
public class ManageViewModel
{
public LocalPasswordModel PasswordModel { get; set; }
public LocalEmailAddressModel EmailAddressModel { get; set; }
}
What I was trying to do is make it where if they access multiple pages on Manage such as /Manage/Password and /Manage/EmailAddress and according to those links, it will post a different page. Now first of all, is this the proper way of doing it / is it fine? Second, if it is, I was trying to pass a Sucess message after they change their email to the /Manage/EmailAddress page, but the ViewBag.StatusMessage is not outputting anything on my HTML page. Why is that?
I did a little more research and here are my findings (tell me if it's correct or not). So I should edit the route settings and so something like:
routes.MapRoute(
name: "Manage",
url: "Account/{controller}/{action}/{id}",
defaults: new { controller = "Manage", action = "Index", id = UrlParameter.Optional}
);
And just make a new controller called Manage and create new Functions inside the controller for different pages like ChangeEmail and ChangePassword?
ViewBag and ViewData are only valid for the current request, they wont survive if you do a redirect RedirectToAction. Use TempData instead.
Check the following: ViewBag, ViewData and TempData
If you want your URLs to be /Manage/Password and /Manage/EmailAddress, you dont need to add a new MapRoute, the default one will work. Just use ManageController for you controller class name and "Password" and "EmailAddress" for your function names:
class ManageController
{
public ActionResult Password(...)
{
Related
I have a controller in my .Net Core 3.1 app that calls another controller action. Before I added a RedirectToAction in the called controller everything worked fine. I now want that controller to redirect to an error page if appropriate, otherwise back to where it was called in the original controller.
Here is the code in my controller:
public ActionResult Edit(IFormCollection collection, string saveClose)
{
//do stuff
LoadArrayDataForDB(Id, collection["names"].ToString(), collection["places"].ToString(), collection["countries"].ToString());
//do more stuff
return View(IVM);
}
//This was the Original LoadArrayDataForDB action
public void LoadArrayDataForDB(string id, string names, string places, string countries)
{
//add error checking
_adoSqlService.DeleteAffectedNames(id);
_adoSqlService.DeleteSelectedPlaces(id);
_adoSqlService.DeleteCountries(id);
}
//This is how I need to change the LoadArrayDataForDB action
public void LoadArrayDataForDB(string id, string names, string places, string countries)
{
//add error checking
string done = _adoSqlService.DeleteAffectedNames(id);
if (done != "Successful")
{
return RedirectToAction("Error", "Home", new { errordescription = "Unable to delete names" });
}
done = _adoSqlService.DeleteAffectedPlaces(id);
if (done != "Successful")
{
return RedirectToAction("Error", "Home", new { errordescription = "Unable to delete places" });
}
done = _adoSqlService.DeleteAffectedCountries(id);
if (done != "Successful")
{
return RedirectToAction("Error", "Home", new { errordescription = "Unable to delete countries" });
}
//etc....
//Just return back to next step in the Edit action
}
How can I do what I need to do?
Change your load function to this
[NonAction]
private string LoadArrayDataForDB( id, string names, string places, string countries)
{
//add error checking
string done = _adoSqlService.DeleteAffectedNames(id,names); // I guess you
// have to use names too
if (done != "Successful") return "Unable to delete names";
done = _adoSqlService.DeleteAffectedPlaces(id,places);
if (done != "Successful")return "Unable to delete places";
done = _adoSqlService.DeleteAffectedCountries(id,contries);
if (done != "Successful") return "Unable to delete countries";
//etc....
return string.Empty
)
or if you want to try all of them before return you can use this
var errMsg=string.Empty;
string done = _adoSqlService.DeleteAffectedNames(id,names); // I guess you
// have to use names too
if (done != "Successful") errMsg= "Unable to delete names";
done = _adoSqlService.DeleteAffectedPlaces(id,places);
if (done != "Successful") errMsg+= " Unable to delete places";
done = _adoSqlService.DeleteAffectedCountries(id,contries);
if (done != "Successful") errMsg+= "Unable to delete countries";
if(!string.IsNullOrEmpty(errMsg) return errMsg;
//etc....
return string.Empty
Change your action:
public ActionResult Edit(IFormCollection collection, string saveClose)
{
//do stuff
var errorDescription= LoadArrayDataForDB(intid, collection["names"].ToString(), collection["places"].ToString(), collection["countries"].ToString()))
if (! string.IsNullOrEmpty(errorDescription) )
return RedirectToAction("Error", "Home", new { errorDescription });
//do more stuff
return View(IVM);
}
I have below controllers:
// GET: /MaterialPaymentRequestSbuItem/CreateChild
public ActionResult CreateChild(int? parentId)
{
if (parentId==null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var parenetRequest = (from request in db.MaterialPaymentRequests
where request.Id==(int)parentId
select request);
ViewBag.MaterialPaymentRequestId = new SelectList(parenetRequest, "Id", "Description", (int)parentId);
ViewBag.ParentID = parentId;
if (parenetRequest!=null && parenetRequest.First()!=null)
ViewBag.ParentTitle = parenetRequest.First().Description;
return View();
}
// POST: /MaterialPaymentRequestSbuItem/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateChild([Bind(Include = "Id,Name,Unit,UnitPrice,MaterialPaymentRequestId,Quantity")] MaterialPaymentRequestSubItem materialpaymentrequestsubitem)
{
if (ModelState.IsValid)
{
try
{
db.MaterialPaymentRequestSubItems.Add(materialpaymentrequestsubitem);
db.SaveChanges();
}
catch (Exception e)
{
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
updateTotalPriceOfParentPaymentRequest(db, db.MaterialPaymentRequests.Find(materialpaymentrequestsubitem.MaterialPaymentRequestId));
return RedirectToAction("List", new { id = materialpaymentrequestsubitem.MaterialPaymentRequestId });
}
ViewBag.MaterialPaymentRequestId = new SelectList(db.PaymentRequests, "Id", "Description", materialpaymentrequestsubitem.MaterialPaymentRequestId);
//need to becheked
ViewBag.ParentID = materialpaymentrequestsubitem.MaterialPaymentRequestId;
if (Request != null && Request["parentID"] != null)
{
try
{
int id = Int32.Parse(Request["parentID"]);
ViewBag.ParentTitle = db.MaterialPaymentRequests.Find(id).Description;
}
catch(Exception e)
{
}
}
return View(materialpaymentrequestsubitem);
}
My main problem is that user is allowed to select model.MaterialPaymentRequestId from drop-down and he/she may leave it with no value selected. MaterialPaymentRequestId is used in first controller (befor post) to find Parent title from db and pass it to view using ViewBag, however if user does not select MaterialPaymentRequestId dropdown items, after postback, I lost MaterialPaymentRequestId. Currently I read Request variable and look inside url to find parameters to lookup for parentID.
My url calls are like http://localhost:46813/Admin/MaterialPaymentRequestSbuItem/CreateChild?parentId=23.
However this practice seems like a bad practice than I cannot pass v variable between two controller methods a process like this:
Controller method(get) ---> View ---> Controller method(post)
Currently I feel a bit stuck in MVC!
i hope i got your point right, you can use an #Html.hiddenfor(model => model.ParentId) in your view it will store ParentId value from query string and when user submits form will be posted to POST method so you dont need to look into url to get it. use a viewmodel with ParentId property and do as below
public class MaterialPaymentsViewModel {
//other properties
public int ParentId {get;set;}
}
public ActionResult CreateChild(int? parentId)
{
var model = new MaterialPaymentsViewModel{ParentId = parentId};
//other stuff
return View(model);
}
//View
#using (Html.beginform()){
//
#html.hiddenfor(m => m.ParentId)
//submit
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateChild(MaterialPaymentsViewModel model)
{
if(model.ParentId != null)
{
//
}
//
}
I have this Model
public class ModelVM
{
private string _rD;
[Required]
public string RD
{
get
{
return _rD;
}
set
{
_rD = RCodes.Contains(value)? value : null;
}
}
private static List<string> RCodes = new List<string>
{
"OK",
"OTHER",
"ANOTHER"
};
}
In my MVC Controller
public class MyController : Controller
{
public ActionResult Index(ModelVM modelVM, FormCollection collection)
{
if (!ModelState.IsValid)
return Json(new
{
Result = "ERROR",
Message = "Missing fields."
});
return Json("OK");
}
}
I send: { RD: "Whatever" }
And in debugging ModelState.IsValid=true. I have a similar code on a WebApi Controller and works as I expect (modelstate.valid=false)
Do you have some ideas why MVC is doing that? or what is wrong with my code?
ModelState.IsValid tells you if any model errors have been added to ModelState.
In this case it is valid because there are no client side errors in the provided data that would affect ModelState.
You said...
I sent { RD: "Whatever" }
...which would mean that the model binder will look at the data sent and match the properties with the intended type. From a model binding perspective the [Required] validation was met because when the binder looked at the route value dictionary for the required property RD, it was provided by the client in the incoming data.
If you want to manually invalidate the state you can...
public ActionResult Index(ModelVM modelVM, FormCollection collection)
{
if(ModelState.IsValid) {
if(modelVM.RD == null) {
ModelState.AddModelError("RD", "RD is invalid.");
}
}
if (!ModelState.IsValid)
return Json(new
{
Result = "ERROR",
Message = "Missing fields."
});
return Json("OK");
}
I have this controller:
[Authorize]
public class CheckoutController : Controller
{
ShoppingCartContext storeDB = new ShoppingCartContext();
const string PromoCode = "FREE";
[HttpPost]
public ActionResult AddressAndPayment(FormCollection values)
{
var order = new Order();
TryUpdateModel(order);
try
{
if (string.Equals(values["PromoCode"], PromoCode,
StringComparison.OrdinalIgnoreCase) == false)
{
return View(order);
}
else
{
order.Username = User.Identity.Name;
order.OrderDate = DateTime.Now;
//Save Order
storeDB.Orders.Add(order);
storeDB.SaveChanges();
//Process the order
var cart = Models.ShoppingCart.GetCart(this.HttpContext);
cart.CreateOrder(order);
return RedirectToAction("Complete",
new { id = order.OrderId });
}
}
catch
{
//Invalid - redisplay with errors
return View(order);
}
}
public ActionResult Complete(int id)
{
// Validate customer owns this order
bool isValid = storeDB.Orders.Any(
o => o.OrderId == id &&
o.Username == User.Identity.Name);
if (isValid)
{
return View(id);
}
else
{
return View("Error");
}
}
}
And I have created a View called AddressAndPayment under Checkout, so it goes to localhost/Checkout/AddressAndPayment but I only get a 404 error, even if I right click on the View and click on view in Page Inspector. I don't know why its not even showing the view when it is created.
You need a corresponding HttpGet method, as your current one only accepts a HttpPost request. Add the following:
[HttpGet]
public ActionResult AddressAndPayment()
{
return View();
}
I have differnt controllers in my Website, Some of them are in WebSite/Controller folder and some are in Website/Area/Test/Controllers.
When I hit Website/Controller/Home/Index, I want to redirect the user to Website/Area/Test/Controller/Home/Index with Query string PArameter.
Here's is my first Controller
namespace mySite.Controllers
{
public partial class HomeController : BaseFrontController
{
public virtual ActionResult Index()
{
var issuburl = channelRepository.GetChannelByUrl('UserID');
if (issuburl != null)
return Redirect("~/Areas/Test/Controllers/Index");
return View();
}
}
}
and here's my Second Controller
namespace mySite.Areas.Test.Controllers
{
public partial class HomeController : BaseTestController
{
public virtual ActionResult Index(string param)
{
var chn = rep1.GetChannel(param);
if (chn != null)
{
model.Chn = chn;
}
else return Redirect("~/Error/Index");
return View();
}
}
}
my Error Controller is in Mysite/Controller folder and I can access it inside MySite/Area/Test/Controller, but How can I access Mysite/Controller controller inside
MySite/Area/Test/Controller
Below code is not working
return Redirect("~/Areas/Test/Controllers/Index");
Have you tried?
return RedirectToAction("Index", "controller", new { area = "Test" });
It is using RedirectToAction
To specify different parameters you can use
return RedirectToAction("Index", "controller", new { area = "Test", yourParam1 = "param1", yourParam2 = "param2" });
try this one:
return RedirectToAction("Index", "controller", new { area = "Test" });