I am trying to return to view from a controller that has both querystring and model
return View("BillingReport.aspx/?report=" + fc["report_selector"], bilDet);
but this gives me a runtime error of page not found as it appends .aspx etc at the end of the url.
RedirectToAction() doesnt have an option to do it.
Is there a way to do it or does mvc3 limit us to using either a query string or a model
MVC does not support what you are looking for,
But I dont understand why do you want to Redirect To a URL with ModelValues.
Any redirection is a GET request, so you can construct the model and return View from that action.
View() expects a view name and model associated with it.
Redirect() or RedirectToAction() are used to redirect the url to another controller/action. So you can not pass a model.Even if you will try to pass model it will append model properties as querystring parameters.
Here is a reason why you would want use the model and the querystring: the querystring allows you to give user way to save URL with state information. The model allows you to pass a lot of non-flattened data around. So here is I think how to do this in MVC 5 (maybe does not work for older versions, but probably does):
Use 2 actions rather than 1 for the view. use first one to set querystring via RedirectToAction. Use second action to return model to the view. And you pass the model from the first action to the second action via session state. Here is example code:
public ActionResult Index(string email){
Session["stuff"]=Load(email);
return RedirectToAction("View1action", new { email = email, color = "red" });
}
public ActionResult View1action(string email){
return View("View1",(StuffClass)Session["stuff"]);
}
I agree with Manas's answer and if I were you I would consider changing the design if possible. As a side note, the following technique is possible:
TempData["bilDet"] = bilDet;
return RedirectToAction(....); // your controller, action etc.
On the action you can then retrieve your TempData. TempData will automatically be removed.
But also check out: ASP.NET MVC - TempData - Good or bad practice
Related
I'm developing a large ASP.NET Core 2 web application but I still get confused with URLs.
When fist learning, I thought URL's took on the name of the View, but discovered they come from the Controller method.
Here is an example to convey my issue:
I have a controller method named Daysheet, which returns a view and model with the same name.
In the Daysheet view, I call various controller methods from Javascript to perform specific actions. One of them is called AssignStaff which takes two integer parameters.
In the AssignStaff method I again return the Daysheet view with model, but now my URL is "AssignStaff"!
I can't just do a redirect because the whole Daysheet model is not being passed to the AssignStaff method.
I have many situations like this where after calling an action, I end up with another URL that I don't want.
UPDATE/EDIT
Thanks for assistance and apologies if my explanation is confusing. I simply have a view called Daysheet that uses a model. I want to call various controller methods to perform various actions, but I want to stay on the "Daysheet" view/URL.
As mentioned, I can't just redirect because in the action method I no longer have the whole model from the Daysheet view. Also, if I redirect I can't pass the whole model because that causes an error saying the header is too long. I think my only choice may be to use ajax for the actions so that the URL doesn't change.
When you just do Return View("") name in a Controller Action, the URL will be the name of the Action you are using.
If you want to redirect to some specific Action, that will help to make sure the Url matches to where you are. You might want to read more about it here.
To do so, use:
RedirectToAction()
The URLs your application responds to are called "routes", and they are either created by convention or explicitly. The default is by convention, of course, which is a URL in the form of /{controller=Home}/{action=Index}. Index is the default action if that portion of the route is left off, so a request to /foo will by convention map to FooController.Index. HomeController is the default controller, so an empty path (e.g. http://sample.com) will by convention invoke HomeController.Index.
Razor Pages have their own conventions. These do somewhat follow the file system, but exclude the Pages part of the path. So a Razor Page like Pages/Foo/MyRazorPage.cshtml, will load up under /Foo/MyRazorPage.
There there is the Route attribute, which allows you to specify a totally custom route. This attribute can be applied to a controller class and individual actions in the class. For example:
[Route("foo")]
public class MyAwesomeController
{
[Route("bar")]
public IActionResult MyAwesomeAction()
{
return View();
}
}
With that, a request to /foo/bar will actually invoke MyAwesomeController.MyAwesomeAction.
I am using ASP.NET MVC C#. I have my view with the following line of code:
#using (Html.BeginForm("TastingParty", "Contact"))
I am submitting to a different controller and action than the current page is on so I had to add a custom controller and action in that line. Now it gets to my controller and at the bottom of the method I call "return View();" so it will display the same page again. But it is looking for that view in the Contact controller because that is where I sent it.
How do I return the original View that it came from? Hopefully that all makes sense.
return RedirectToAction(Action,Controler)
You are searching for this?
You can either use return RedirectToAction(...
http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.redirecttoaction(v=vs.118).aspx
Or, if you want to be able to reuse the view across different controllers, you can place it in the Views\Shared folder.
In the latter case, you then have the ability to return the posted model back out to that view.
public ActionResult YourAction(YourViewModel postedModel) {
// do something
return View(postedModel);
}
And again, if you want this to be a shared view, you could place it under Views\Shared\YourAction.cshtml.
I am current working on a MVC C# application and have a HttpPost ActionResult which adds a new role to a user. I want to use this method throughout my application.
The problem I have is once this method has run I want it to return to the previous ActionResult and as I want to reuse this method I can not set the return view to a set view. Is there a way to set the return to the previous View?
Many Thanks
Andrew
You could pass a returnUrl when invoking the POST controller action:
[HttpPost]
public ActionResult Foo(string returnUrl)
{
.... do something
return Redirect(returnUrl);
}
This way you can POST to this controller action from different parts of the application and every time you will provide the return url you want to get redirected to once it has finished processing. This is for example how the LogOn action in the default template works. You may take a look at it.
It will by default return the view which is the same name as the action, not which view the form was submitted from.
If you want to return a specific view then you can do:
return View("ViewName",Model);
If you want to return the view that submitted the form you will need to tell the action where it came from, so you can add a property to your viewmodel that was set in the view. Then you could do:
return View(ViewModel.ViewName,ViewModel);
You might be able to do something nasty by digging the viewname out of the request object, but that is so horrible I am not going to even tell you where to begin.
You can create an ActionFilter which uses a cookie to store the current route values. It loads the previously saved cookie in OnActionExecuting and stores the current route valeus in OnActionExecuted. But it will fail for everyone that has disabled cookies.
You could also use the HTTP header Referrer. But it isn't set all the time.
There are in other words no reliable way to achieve what you want (without using a parameter as described by Darin). At least not a way which is worth the trouble.
In my controller say ControllerFirst I am setting a ViewBag property by the below line.
ViewBag.PreviousPage="ThisIsComingFromControllerFirst";
return RedirectToAction("ControllerSecond", "Home");
Now from here:
public ActionResult ControllerSecond()
{
return View();
}
I am trying to use Viewbag in the ControllerSecond by the following
View: ControllerSecond.cshtml
#if(ViewBag.PreviouPage == "SomeValue")
{
//do this
}
But ViewBag.PreviousPage value is null.
Please let me know why its null, what could I do to get the value in my view from the ControllerFirst.
I have done this one using Session, but We don't want to sessions..
Any other options?
To answer your first question, ViewBag is a more convenient form of ViewData that uses dynamic objects rather than generic ones. As with ViewData, ViewBag only exists for the life of a single request, thus it is only available between the ActionMethod and it's view. It is not available between different ActionMethods.
Think of it like an intercom system in a home. You can send messages to other parts of the home, but you can't send a message to a neighbors home.
The only other options you have are:
Use Session
Use TempData (which also uses session)
Use a Cookie
Use a querystring parameter
Use an intermediate table in your database
Post to ActionMethod
ViewBag (and ViewData) are objects for accessing extra data (i.e., outside the data model), between the controller and view.
If your data have to persist between two subsequent requests you can use TempData.
However, TempData is by default stored in the session.
So, if you don't want to use sessions, you could use cookies and somehow duplicate a bit of what session does for you, as MikeSW suggested.
When to use ViewBag, ViewData, or TempData in ASP.NET MVC 3 applications
Use TempData instead of ViewBag
You can use a cookie and somehow duplicate a bit of what session does for you.
if i start off on a Detail page:
http:\\www.mysite.com\App\Detail
i have a controller action called Update which normally will call redirectToAction back to the detail page. but i have an error that is caught in validation and i need to return before redirect (to avoid losing all of my ModelState). Here is my controller code:
public override ActionResult Update(Application entity)
{
base.Update(entity);
if (!ModelState.IsValid)
{
return View("Detail", GetAppViewModel(entity.Id));
}
return RedirectToAction("Detail", new { id = entity.Id })
but now I see the view with the validation error messages (as i am using HTML.ValidationSummary() ) but the url looks like this:
http:\\www.mysite.com\App\Update
is there anyway i can avoid the URL from changing without some hack of putting modelstate into some temp variables? Is there a best practice here as the only examples i have seen have been putting ModelState in some tempdata between calling redirectToAction.
As of ASP.NET MVC 2, there isn't any such API call that maintains the URL of the original action method when return View() is called from another action method.
Therefore as such, the recommended solution and a generally accepted convention in ASP.NET MVC is to have a corresponding, similarly named action method that only accepts a HTTP POST verb. So in your case, having another action method named Detail like so should solve your problem of having a different URL when validation fails.
[HttpPost]
public ActionResult Detail(Application entity)
{
base.Update(entity);
if (ModelState.IsValid)
{
//Save the entity here
}
return View("Detail", new { id = entity.Id });
}
This solution is in line with ASP.NET MVC best practices and also avoids having to fiddle around with modestate and tempdate.
In addition, if you haven't explored this option already then client side validation in asp.net mvc might also provide for some solution with regards to your URL problem. I emphasize some since this approach won't work when javascript is disabled on the browser.
So, the best solution would be have an action method named Detail but accepting only HTTP POST verb.
The problem here is actually caused by your implementation. This doesn't answer your question, but it describes where you've gone wrong in the first place.
If you want a page that is used to update or edit an item, the URL should reflect this. For example.
You visit http:\www.mysite.com\App\Detail and it displays some information about something. That is what the URL describes it is going to do. In your controller, the Detail() method would return the Detail.aspx view.
To edit an item, you would visit http:\www.mysite.com\App\Edit and change the information you wish to update, the form would post back to the same URL - you can handle this in the controller with these methods:
[HttpGet]
public ActionResult Edit() {
MyModel model = new MyModel();
...
return View(model);
}
[HttpPost]
public ActionResult Edit(MyModel model) {
...
if (ModelState.IsValid) {
// Save and redirect
...
return RedirectToAction("Detail");
}
return View(model);
}
If you ever find yourself doing this...
return View("SomeView", model);
You are making your own life harder (as well as breaking the principles behind URLs).
If you want to re-use part of a view, make it a partial view and render it inside of the view that is named after the method on the controller.
I apologise that this potentially isn't very helpful, but you are falling into an MVC anti-pattern trap by displaying the same view from a differently named method.
As #Malcolm sais, best practice is to put ModelState in TempData, but don't do it manually! If you'd do this manually in every controller action where it's relevant, you would introduce immense amounts of repeated code, and increase the maintenance cost vastly.
Instead, implement a couple of attributes that do the job for you. Kazi Manzur has an approach (scroll down to the end of the post) that has been widely spread, and Evan Nagle shows an implementation with tests that is essentially the same as Kazi's, but with different names. Since he also provides unit tests that make sure the attributes work, implementing them in your code will mean little or no maintenance cost. The only thing you'll have to keep track of is that the controller actions are decorated with the appropriate attributes, which can also be tested.
When you have the attributes in place, your controller might look something like this (I deliberately simplified, because I don't know the class you inherit from):
[HttpPost, PassState]
public ActionResult Update(EntityType entity)
{
// Only update if the model is valid
if (ModelState.IsValid) {
base.Update(entity);
}
// Always redirect to Detail view.
// Any errors will be passed along automagically, thanks to the attribute.
return RedirectToAction("Detail", new { id = entity.Id });
}
[HttpGet, GetState]
public ActionResult Detail(int id)
{
// Get stuff from the database and show the view
// The model state, if there is any, will be imported by the attribute.
}
You mention that you feel putting ModelState in TempData feels like a "hack" - why? I agree with you that doing it with repeated code in every single controller action seems hacky, but that's not what we're doing here. In fact, this is exactly what TempData is for. And I don't think the above code looks hacky... do you?
Although there are solutions to this problem that might appear simpler, such as just renaming the action method to preserve the URL, I would strongly advise against that approach. It does solve this problem, but introduces a couple of others - for example, you'll still have no protection against double form submission, and you'll have pretty confusing action names (where a call to Detail actually changes stuff on the server).
The best practice you ask for is actually what you explained not to do: putting modelstate into tempdata. Tempdata is meant for it, that's why I would not call it a hack.
If this is to much repetitive code you could use the attribute modeldatatotempdata of MVCContrib. But the store is still TempData.