Returning to current view after actions in _Layout.cshtml - asp.net-mvc-3

I'm trying to wrap my head around MVC.NET 3.
I use a _Layout.cshtml as base (structure, navigation). As part of the layout I want to display two links used for changing language/localization.
These should be displayed and clickable no matter what page is viewed, and after changing the localization I want to reload the view that called the action. So the page that the customer is looking at will be reloaded, with new localization set.
One way is to copy and paste the localization-changing action in each of the sites controllers, but is there no easier and more elegant way?
I tried creating a specific controller that handles the localization changing, but can't figure out how to return the viewer to the previous controller.
Perhaps this is easier accomplished with jquery?
This is the DIV from the _Layout file with the language changing buttons. It calls the action in the current controller, which means I have to define it in each of the site's controllers. (The good thing is the view that is returned is always correct.)
<div id="top>
#using (Html.BeginForm())
{
<button id="sv_flag" name="localization" title="#Resources.Global.Sv_Flag_Hover" value="sv-SE" />
<button id="en_flag" name="localization" title="#Resources.Global.En_Flag_Hover" value="en-GB" />
}
</div>
I also tried using a specific controller for this, but cannot think of how I could return to the current view afterwards? Like so:
#using (Html.BeginForm("LocalizationAction", "LocalizationController"))
...
Edit
Now using the suggestion from Darin, I send in the controller and action values from the layout page:
#using (Html.BeginForm("SetLocalization", "Localization",
new { returnController = #ViewContext.Controller.ValueProvider.GetValue("controller").RawValue,
returnAction = #ViewContext.Controller.ValueProvider.GetValue("action").RawValue }))
...
But I cannot get the localization changes to work, my controller action looks like this:
public ActionResult SetLocalization(string localization, string returnController, string returnAction)
{
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(localization);
return RedirectToAction(returnAction, returnController);
}

You could pass a returnUrl:
#using (Html.BeginForm("LocalizationAction", "LocalizationController", new { returnUrl = Request.Url.AbsoluteUri }))
{
...
}
and inside your LocalizationAction redirect to this url:
public ActionResult LocalizationAction(string returnUrl)
{
... do your localization stuff and once you are done get back:
return Redirect(returnUrl);
}
obviously you could do a little checking before blindly redirecting. Things like whether the returnUrl parameter is not empty and whether it belongs to your domain. You may take a look at how the default AccountController does that once it authenticates a user.

return Redirect(Request.UrlReferrer.ToString());
public ActionResult Change(String LanguageAbbrevation)
{
if (LanguageAbbrevation != null)
{
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(LanguageAbbrevation);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(LanguageAbbrevation);
}
HttpCookie cookie = new HttpCookie("Language");
cookie.Value = LanguageAbbrevation;
Response.Cookies.Add(cookie);
return Redirect(Request.UrlReferrer.ToString());
}

I found a completely different (and much easier and elegant) solution to my problem.
I simply created a BaseController, that holds the action for changing the localization.
Then all controllers I add to the site inherit from this BaseController. This gives a single location for the code and does not require sending any return parameters, etc.
BaseController:
public class BaseController : Controller
{
[HttpPost]
public ActionResult Index(string localization)
{
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(localization);
return View();
}
}
Each of the site's Controllers then only need to inherit from it, and then mind their own actions:
public class ApplicationsController : BaseController
{
//
// GET: /Applications/
public ActionResult Index()
{
return View();
}
}
...

Related

static content pages in an MVC web application

I have just created my first MVC 3 project for a database search using EF db first, but the search is only a part of a big website most of the pages will just contain some text and images.
My question is basically about these pages which on the website would be .aspx, and the code behind would have nothing at all.
They use a master page and some user controls - my guess is that's the reason our front end person made them aspx not html.
I need to convert/include her pages into my project (I don't want to go back to stored procedures and listview after having used EF and Linq, plus I don't have time).
I know of one possible way: create a controller for each of the main menu items, then add ActionResult named for each of the submenu items returning View(), then create respective views.
public class LearnAboutStandardsController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult ITSStandardsBackground()
{
return View();
}
public ActionResult ResearchInitiatives()
{
return View();
}
So my static content pages will become Views.
It's working, I just want to do it for the rest of the pages and modify the links in the text of these pages.
Is there any other way to handle these pages?
There is no logic behind these pages.
I know this was not a perfect project for the MVC pattern with so much static content, but I had my reasons for it.
I handle this with an "StaticContent" controller:
StaticContentController.cs
public class StaticContentController : Controller
{
public ActionResult About()
{
return View();
}
public ActionResult Services()
{
return View();
}
public ActionResult Portfolio()
{
return View();
}
}
Add the code below your route config to handle the static routes:
routes.MapRoute(
"StaticContent",
"{action}",
new { controller = "StaticContent" },
new { action = "About|Services|Portfolio" } // Add more here
);
You're set.
If you need more pages just add the action in the StaticController and adjust your StaticContent MapRoute.
Personally, I would have controllers with simple actions that just render views. That way if you do add more features later you're already set up. And if you want to add security or caching it's a lot easier and more consistent.
You can still use WebForms (with the new Friendly URLs feature if you want "pretty" URLs) for the "static" pages. Or you can use Web Pages with Razor and create CSHTML files for the static content.

How to redirect from one view to another view in MVC3?

Hello everyone I would like to ask how to redirect from one view to another. Here is my view
#model IEnumerable<test.Models.contents>
#using test
#if(Request.IsAuthenticated) {
<text>Welcome<strong>#User.Identity.Name</strong>
</text>
}
else
{
???
}
Don't do any redirects inside the view. That's not its responsibility. The responsibility of a view is to display data that is passed to it from the controller action under the form of a view model.
Do this redirect inside the controller action that is rendering this view. For example you could decorate it with the [Authorize] attribute. This way if the user is not authorized he will be redirected to the loginUrl you specified in your web.config:
[Authorize]
public ActionResult SomeAction()
{
return View();
}
and if you wanted to redirect to some particular view you could simply write a custom Authorize attribute and override the HandleUnauthorizedRequest method to specify the controller and action you wish to redirect to in case of user not being authenticated:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
var values = new RouteValueDictionary(new
{
controller = "SomeController",
action = "NotAuthorized"
});
filterContext.Result = new RedirectToRouteResult(values);
}
}
and then decorate your action with it:
[MyAuthorize]
public ActionResult SomeAction()
{
return View();
}
Now inside the corresponding view you don't need to perform any tests. It is guaranteed that if you got as far as rendering this view the user is authenticated and you could welcome him directly:
#model IEnumerable<test.Models.contents>
#using test
Welcome <strong>#User.Identity.Name</strong>

Route values disappeare in View .Net MVC3

I have simple controller:
public class TestController : Controller
{
public ActionResult Test(string r)
{
return View();
}
}
I have simple View Test.cshtml:
<h2>#ViewContext.RouteData.Values["r"]</h2>
#using (Html.BeginForm("Test", "Test"))
{
<input type="text" name="r" />
<button>Submit</button>
}
I have route rule in Global.asax:
routes.MapRoute(
null,
"Test/{r}",
new { action = "Test", controller = "Test",
r = UrlParameter.Optional }
);
I want to make such thing: user types route value in input, press submit and controller redirects him to page Test/value. But controller show just page with name Test everytime. ViewContext.RouteData.Values["r"] is empty too. I check in debug, Test action recieves user value of r correctly.
How can I realize my idea?
Thanks.
I'm super late to the party, but just wanted to post a solution for reference. Let's assume that this form has more than just a strong as it's input. Assuming there are other inputs, we can wrap up the inputs of the form into a class in our model, called TestModel whose properties maps to the id's of the form's inputs.
In our post, we redirect to the get, passing in the route values we need in the URL. Any other data can then be shuttled to the get using a TempData.
public class TestController : Controller
{
[HttpGet]
public ActionResult Test(string r)
{
TestModel model = TempData["TestModel"] as TestModel;
return View(model);
}
[HttpPost]
public ActionResult Test(string r,TestModel model) //some strongly typed class to contain form inputs
{
TempData["TestModel"] = model; //pass any other form inputs to the other action
return RedirectToAction("Test", new{r = r}); //preserve route value
}
}
You cannot do this without javascript. There are two types of methods that exist when submitting a <form>: GET and POST. When you use POST (which is the default), the form is POSTed to the url but all data entered in input fields is part of the POST body, so it is not part of the url. When you use GET, the input fields data is part of the query string but of the form /Test?r=somevalue.
I wouldn't recommend you trying to send user input as part of the path but if you decide to go that route you could subscribe to the submit event of the form and rewrite the url:
$('form').submit(function() {
var data = $('input[name="r"]', this).val();
window.location.href = this.action + '/' + encodeURIComponent(data);
return false;
});
As far as you are saying to post the form to Html.BeginForm("Test", "Test") you will be always posted back to the same page.
A solution could be to use an explicit Redirect to the action using 'RedirectToAction' (in view) or you can use javascript to change the form's action:
<input type="text" name="r" onchange="this.parent.action = '\/Test\/'+this.value"/>

ASP.Net Mvc 3 Url.Action method uses parameter values from previous request

When Urls are autogenerated using the Url.Action helper, if a page contains a line similar to
#Url.Action("Edit","Student")
is expected to generate a url like domain/student/edit and its working as expected.
But if the requested url contains some parameters, like domain/student/edit/210, the above code uses these parameters from the previous request and generates something similar even though I've not provided any such parameter to the Action method.
In short, if the requested url contains any parameters, any auto generated links of the page (served for that request) will include those parameters as well no matter if I specify them or not in the Url.Action method.
What's going wrong?
Use Darin's answer from this similar question.
#Url.Action("Edit","Student", new { ID = "" })
Weird, can't seem to reproduce the problem:
public class HomeController : Controller
{
public ActionResult Index(string id)
{
return View();
}
public ActionResult About(string id)
{
return View();
}
}
and inside Index.cshtml:
#Url.Action("About", "Home")
Now when I request /home/index/123 the url helper generates /home/about as expected. No ghost parameters. So how does your scenario differs?
UPDATE:
Now that you have clarified your scenario it seems that you have the following:
public class HomeController : Controller
{
public ActionResult Index(string id)
{
return View();
}
}
and inside Index.cshtml you are trying to use:
#Url.Action("Index", "Home")
If you request /home/index/123 this generates /home/index/123 instead of the expected /home/index (or simply / taken into account default values).
This behavior is by design. If you want to change it you will have to write your own helper which ignores the current route data. Here's how it might look:
#UrlHelper.GenerateUrl(
"Default",
"index",
"home",
null,
Url.RouteCollection,
// That's the important part and it is where we kill the current RouteData
new RequestContext(Html.ViewContext.HttpContext, new RouteData()),
false
)
This will generate the proper url you were expecting. Of course this is ugly. I would recommend you encapsulating it into a reusable helper.
Use ActionLink overload that uses parameters and supply null
You could register custom route for this action for example:
routes.MapRoute("Domain_EditStudentDefault",
"student/edit",
new {
controller = MVC.Student.Name,
action = MVC.Student.ActionNames.Edit,
ID = UrlParameter.Optional
},
new object(),
new[] { "MySolution.Web.Controllers" }
);
you then could use url.RouteUrl("Domain_EditStudentDefault") url RouteUrl helper override with only routeName parameter which generates url without parameters.

Weird MVC Issue

I have this code and I can't understand why it works this way
I have a model and view which is arbitrary and a very simple (but weird) controller
Here is my controller:
public partial class RouteController : Controller
{
[HttpGet]
public virtual ActionResult Create()
{
Create create = new Create();
return View("Create", create);
}
[HttpPost]
public virtual ActionResult Create(Create route)
{
return Create();
}
}
The first create method loads the view as normal. When the view posts back it runs the 2nd action which runs the first (as expected). The wierd part is the view is (re-)loaded with my previously entered data with errors (if any). I dont understand this because my model is empty. I was expecting it to post back with the same form as if it was loaded for the first time but with errors possibly.
Please explain.
That's the normal behavior of HTML helpers and it is by design. They first look at values contained in the ModelState and after that in the actual model. If you intend to modify some values on the model in a POST action you need to remove them from modelstate first:
For example:
[HttpPost]
public virtual ActionResult Create(Create route)
{
ModelState.Remove("SomeProperty");
route.SomeProperty = "some new value";
return View(route);
}
If you intend to completely modify everything as in your example you could clear the modelstate entirely:
[HttpPost]
public virtual ActionResult Create(Create route)
{
ModelState.Clear();
return Create();
}
Another possibility is to write your own TextBoxFor, HiddenFor, CheckBoxFor, ... helpers that will use the value in the model and not the one in the model state. Or yet another (non-recommended) possibility:
<input type="text" name="SomeProperty" value="#Model.SomeProperty" />
Of course in this case client validation among other things provided by the standard helpers won't work.

Resources