How to redirect from one view to another view in MVC3? - asp.net-mvc-3

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>

Related

Returning to current view after actions in _Layout.cshtml

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();
}
}
...

How do I perform error handling in ASP.NET MVC 3 controller?

I have an Account controller which have:
LoginForm (get action)
Login (post action)
RegisterForm (get action)
Register (post action)
In another controller's index action i use render them as:
Html.RenderAction("RegistrationForm", "Acount");
Html.RenderAction("LoginForm ", "Acount");
Everything works ok and I can register a new user, login and validate with unobtrusive validation.
The problem is when do some back-end validation in Register/Login action and if there is an error I don't know how to transfer the error to be rendered.
I've tried with PRG pattern and it works ok. I get the error displayed on the form with the preserved data, but PRG is not the way to do it.
What is an alternative solution to this problem without using ajax to validate or move those methods in the controller where the RegistrationForm/LoginForms are used?
I want to skip using TempData due to session usage in the background.
EDIT CODE SAMPLE:
class AccountController : SomeBaseController{
[HttpGet]
public PartialViewResult RegistrationForm()
{
return PartialView(new RegisterUser());
}
[HttpPost]
public ActionResult RegisterUser(RegisterUser user)
{
if (ModelState.IsValid)
{
var _user;// create domain user from Register user Model;
var _validationOutput = _userService.DoSomeAwsomeServerSideValidation(_user);// do some custom validation
if (_validationOutput.IsFault)
{
// we preseve tempdata in base controller OnActionExecuted
_validationOutput.ErrorMessages.ForEach(x => ModelState.AddModelError(_validationOutput.ErrorCode, _validationOutput));
// redirect to home controller custom error occured
return RedirectToAction("Index", "Home", user);
}
return RedirectToAction("RegistrationInfo");
}
return RedirectToAction("SomeUserInfoAction");
}
}
class HomeController : SomeBaseController {
Index(){
return View();
}}
HomeControllerMarkup {
#{Html.RenderAction("RegistrationForm", "Acount");}
#{Html.RenderAction("LoginForm", "Acount");}
}
You can manually add errors to your ModelState within your post controller using:
ModelState.AddModelError("", #"You didn't perform task XYZ");
You should then be able to return the view and display the errors in a validation summary:
#Html.ValidationSummary(false, "Login was unsuccessful because...")

why TempData[] doesnt work with IE

İn my MVC3 project, there is plenty of TempData[] that I am using for passing datas between actions. And it works totaly perfect when I use Chrome. But in IE I can't get values of TempData[] items. if anyone knows whats the problem and how can I solve it?`
public class SomeController : Controller
{
public ActionResult SomeAction()
{
TempData["id"] = "someData";
return View();
}
}
public class AnotherController : Controller
{
public ActionResult AnotherAction()
{
string data = Convert.ToString(TempData["id"]);
return View();
}
}
`
You should never return a view from a controller action that stores something into TempData. You should immediately redirect to the controller action that is supposed to use it:
public class SomeController : Controller
{
public ActionResult SomeAction()
{
TempData["id"] = "someData";
return Redirect("AnotherAction", "Another");
}
}
public class AnotherController : Controller
{
public ActionResult AnotherAction()
{
string data = Convert.ToString(TempData["id"]);
return View();
}
}
The reason for this is that TempData survives only for a single additional request. So for example if inside the view you are sending an AJAX request to some controller action (no matter which) and then have a link in this view pointing to the target action, when the user is redirected to this target action TempData will no longer exist since it was lost during the AJAX request done previously.
If you need to store data for longer than a single redirect you could use Session.
If you need to store data for longer than a single redirect you should use Keep or Peek methods.
string data = TempData["id"].;
TempData.Keep("id");
or simply use,
string data = TempData.Peek("id").ToString();
Peek function helps to read as well as advice MVC to maintain “TempData” for the subsequent request.

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"/>

Overriding routevalues for childaction

I have an index view that takes the page parameter like so:
/News?page=2
But in the layout for that view I have this childaction:
#{Html.RenderAction("Index", "Comments", new {page=1, pagesize = 10});}
But the querystring "page" remains 2.. how come? And how to override page for the childaction?
That's because the child action first looks at the original request query string when binding for values and after that the one passed as argument to the RenderAction helper. You could use a different name for this parameter to avoid this ambiguity.
UPDATE:
Unable to reproduce the behavior you are describing.
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[ChildActionOnly]
public ActionResult Test(string page)
{
return Content(page, "text/html");
}
}
View (~/Views/Home/Index.cshtml):
#{Html.RenderAction("Test", "Home", new { page = 1 });}
When querying /Home/Index?page=5 the correct value 1 is shown and the page parameter in the child action is 1.
Obviously if inside your child action you are fetching this value manually from the request it won't work but that's not something you should be doing anyways:
[ChildActionOnly]
public ActionResult Test()
{
string page = Request["page"] as string;
return Content(page, "text/html");
}

Resources