I'm using default route configuration of domain/controller/action.
For instance, www.domain/Appointment/RequestAppointment calls the Appointment controller and RequestAppointment ActionResult.
I've been often asked by our Marketing folks to build redirects for shorter versions of our URLs for use in social media or on billboards.
So, they would like to see www.domain/Appointment/RequestAppointment as www.domain/appointment.
I don't have an issue doing that, in this case I am just:
//Appointment Controller
public ActionResult Index()
{
return RedirectToAction("RequestAppointment");
}
Now, our SEO person is telling me the new URL (www.domain/appointment) is returning a 302 redirect (which I understand) and that is bad for SEO. They would like a 301.
So, I understand I can return the (desired from SEO viewpoint) 301 redirect by using:
//Appointment Controller
public ActionResult Index()
{
return RedirectPermanent("http://www.domain.com/Appointment/RequestAppointment");
}
It works, but is there a better way or a best practice to handle this? I figure it can't be that uncommon, but I can't find anything.
I will also have to do the same thing with about 10 other unique URLs that fall into the www.domain/controller/action/id format that would be redirected to www.domain/promotion.
You don't need to set up redirects for these. You can set up the Routing engine to automatically map a given URL to a specific controller action, even if it doesn't match the given default structure. Keep in mind, these need to be BEFORE the default route. You can add as many of these as you want, and they will return a 200, not even a 301.
routes.MapRoute("Request Appointment",
"appointment", // <= What you want the URL after "www.domain" to be
new { controller = "Appointment", action = "RequestAppointment" });
Bear in mind, with this in place, you would need to have the URL be "appointment/index" if you want to get to the index action. However, the same holds true with any other sort of mapping. But, you can get to the same page with both of the following URLs with no server or client redirects occurring:
www.domain.com/appointment
www.domain.com/appointment/requestappointment
Related
I have a link which uses a query string, and it's always going to the [HttpPost] method instead of the [HttpGet] method.
The error I'm getting is a NullReferenceException on TempData["surveytype"], so I know it's going to Post instead of Get. I have no idea why though.
I found a couple of similar questions here but nothing that resolved my problem. I thought maybe MVC was interpreting this as a form submission and sending to HttpPost because I'm styling the link as a "btn btn-primary" class, but removing that changed nothing.
My link:
Start Response
Controller:
[HttpGet]
public ActionResult Create(int SurveyId)
{
TempData["SurveyId"] = SurveyId;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = ...)] Response response)
{
if (ModelState.IsValid)
{
response.Userid = User.Identity.GetUserId();
response.Id = Guid.NewGuid();
db.response.Add(response);
db.SaveChanges();
TempData["ResponseId"] = response.Id;
int? surveyid = response.SurveyId;
var surveytype = db.surveys.Find(surveyid).surveytype;
TempData["surveytype"] = surveytype;
...
}
}
I think your [HttpGet] makes the routing to confuse - Remove it
public ActionResult Create(int SurveyId)
{
TempData["SurveyId"] = SurveyId;
return View();
}
Now based on the action name the route will take care either it's a get or post - just a solution try it out and let me know if you face same issue
Thanks - Happy coding :)
For starters, you shouldn't manually create links like that. It's very error prone, and you run into situations like this often. You should use the helpers to create the link. For example:
#Html.ActionLink("Start Responses", "Create", "Controllername", new { SurveyId = item.id }, new { #class = "btn btn-primary" })
Next, you should make sure you don't have any custom routing that might be interfering, either with Attribute based routing or by using MapRoute.
The biggest reason to use the helpers is that MVC can access pages from different actual url's, and hard coding a path like that is almost impossible to get right.
For instance, let's say you can access a page at http://exmample.com, http://example.com/Home or http://example.com/Home/Index.
Using a hard coded "../whatever" means that this will translate to the corresponding url's http://example.com/../whatever (obviously not what you want), http://example.com/Home/../Whatever (might be what you want, might not), or http://example.com/Home/Index/../Whatever (this is probably what you want want, but it won't get there unless the users's browser url has this third url to access it).
If you must hard code a link, then you should always use a full root relative url link ("/Home/whatever") instead of relative ("../whatever"), but even that has issues.. what happens if you decide to move your site into a subdirectory of your site, now all your hard coded urls are wrong.
Using Url helpers is always the best way to go in MVC.
My team is building a simple MVC site for very low end "feature" phones. One problem we have is that certain phone aggressively cache HTML, so what the user actually gets to see is not what we intend. An extreme example is, a user registers and gets a "thank you page". Another user on the same device then tries to register. The phone simply serves the cached page without creating a new account.
In the past I've dealt with this by adding a "cache buster" querystring to things I don't want cached, eg all pages will be served in the format
http://domain.com/controller/route?cb=somerandomstringofnumbers
In this case we'd need to do this for all URLs in the site - this includes the URLs auto-generated by Controller actions such as RedirectToAction or Redirect and also the Razor Url.Action, Html.BeginForm, Html.ActionLink etc.
Now obviously I could decorate the Razor HTML helpers (or extend them) and add the argument to an controller action, but it seems to me that because the actual URLs generated by these built in methods are auto-generated from the Controller/Action params passed in, there should be a way to hijack that process.
Unfortunately the MS classes are protected - I'm mostly looking in System.Web.Routing.Routes.
I've tried a few things I've found online but they are not MVC5 (dating back to 2008) and it seems the framework has changed significantly.
eg, from
http://forums.asp.net/t/1216840.aspx?Append+value+to+all+urls+built+by+RouteCollection+GetUrl
public class SessionAppendingRouteHandler : IRouteHandler
{
public IHttpHandler GetHandler(RequestContext context)
{
SessionAppendingHttpHandler handler = new SessionAppendingHttpHandler();
handler.RequestContext = context;
return handler;
}
}
public class SessionAppendingHttpHandler : MvcHandler
{
public override ProcessRequest(RequestContext context)
{
//append your sid here
}
}
// and in the route setup
RouteTable.Routes.Add( new Route
{
Url = "/[controller].mvc/[action]/",
Defaults = new { action = "index" },
RouteHandler = typeof(SessionAppendingRouteHandler)
});
This I cant get to work as the framework has changed too much, but it looks very close to what I would like to achieve.
I feel like I'm in the right area, but I've hit a brick wall.
Any suggestions?
This is quite old, but let me answer based on how I solved a similar problem:
Instead of having it as query string, have the cb as a route value just as action and controller are route values. You can do this by registering a route; for instance:
routes.MapRoute(
name: "CB",
url: "{cb}/{controller}/{action}/{id}",
defaults: new { cb = "3600", area = "", id = UrlParameter.Optional }
);
If the value for cb is not a constant, then you can find a convenient point to set the cb for each user session. A good place will be after a successful login. With this, you'll now need to provide just two custom methods for RedirectToAction and ActionLink. Your implementation will simply package a RouteValueDictionary and then pass it in to MVC's own implementation using the appropriate overloads.
I'm having a little problem i hope someone can help me with.
on ASP.net MVC 4 (C#) i need to be able to edit the parameters of my route from my controller.
example the request url is
MyController/MyAction/param1/param2
now from MyAction I need to edit the returned url so that it displays
MyController/MyAction/Modifiedparam1/Modifiedparam2
The purpose of this is to translate the parameters that i retrieve from my database from language changes.
Please use redirect result for this (in MyAction on some condition):
return RedirectToAction("MyAction", { param1Name = Modifiedparam1, param2Name = Modifiedparam2 };
Basically you cannot modify url in controller. Urls is something send by the browser to invoke some action. You may just say browser to redirect user to another url.
Don't fight with framework. It will fight back sooner or later. Instead follow mvc principles, redirect from controller action or filters/interceptors to do so.
I am learning MVC 3 after hours right now and last night I ran into an issue that seems like it would be very simple to solve but I can't seem to locate a solution for it.
I have a simple contact form. The end user fills out the form and submits it. When they submit the form I redirect the end user to a different action in the same controller which renders an "inquiry submitted" page/view which is basically a "Thank you page".
The controller is setup like so.
public ActionResult ContactUs()
{
return View();
}
[HttpPost]
public ActionResult ContactUs(ContactInfo contactInfo)
{
if (!ModelState.IsValid)
{
return View();
}
//perform some business logic
return RedirectToAction("InquirySubmitted",contactInfo);
}
public ActionResult InquirySubmitted(ContactInfo contactInfo)
{
return View(contactInfo);
}
The problem:
I do not want end users navigating directly to the InquirySubmitted action via the browser.
I only want the ContactUs action in the controller to be able to send users to the InquirySubmitted View.
I have attempted to make the InquirySubmitted action private so that only the controller can call it like so:
private ActionResult InquirySubmitted(ContactInfo contactInfo)
But this produces an error which I fully understand because I am forcing the browser to request InquirySubmitted by using RedirectToAction().
So my question is simply: What is the best "MVC 3 style" solution to this issue.
You will need to put logic in your InquirySubmitted ActionResult in order to prevent users from viewing the page if they are not supposed to.
You are already passing the InquirySubmitted method your model (ContactInfo). Could you simply inspect the data passed to the method and if it is absent then redirect the user to an error page (or some other page of your choice)?
An alternate solution would be to set a boolean in session that indicates that the user completed the "ContactUs" form. Then you could check for that session object within InquirySubmitted.
First, I would have to say.. Who cares if someone can navigate directly to the Inquiry submitted page? Is there any confidential information, or something sensitive there? If not, so what? What does it hurt?
However, if you're determined to do so. The answer to your question of "How to make an action not accessible directly from the browser" is that You can simply use Html.Action() to render the page, and then decorate the action method with a [ChildActionOnly] attribute.
This doesn't actually solve the problem though, since making the action indirectly accessible only answers your question, not solves your problem. Ultimately, you need to redirect the user to a url to load the page, so you will need some logic that determines if they can view the page or not. This is
Not sure if this still applies in MVC3, but in MVC2 it worked.
your global.asax file has your url structuring in it. You can add your InquirySubmitted to the list of urls that isn't accessible there.
I am fairly new to MVC but not sure exactly which Redirect... replaces the standard redirect used in WebForms is the standard Response.Redirect()
For instance, I need to redirect to other pages in a couple of scenarios:
WHen the user logs out (Forms signout in Action) I want to redirect to a login page.
In a Controller or base Controller event e.g. Initialize, I want to redirect to another page (AbsoluteRootUrl + Controller + Action)
It seems that multiple redirects get called in some cases which causes errors, something to do with the fact a page is already being redirected? How can cancel the current request and just redirect?
Update:
The answer to this question (System.Web.Mvc.Controller Initialize) indicates that Initialize should not be used and OnActionExecuting should be used?
Any comments on why Initialize should not be used or why OnAuthorization is not a better option?
More Info:
This blog post (http://blog.wekeroad.com/blog/aspnet-mvc-securing-your-controller-actions/) indicates that OnActionExecuting is useful for authentication (as indicated in the link above) I guess adding this to that event in the base Controller class is fine as every page runs an Action in MVC so shouldn't make much difference and having the ability to redirect should be easier. This does make sense, but it also seems to make sense to me that things could be done in an event before this event and makes we question what those events are for? Will be giving OnActionExecuting a go..
1) When the user logs out (Forms signout in Action) I want to redirect to a login page.
public ActionResult Logout() {
//log out the user
return RedirectToAction("Login");
}
2) In a Controller or base Controller event eg Initialze, I want to redirect to another page (AbsoluteRootUrl + Controller + Action)
Why would you want to redirect from a controller init?
the routing engine automatically handles requests that come in, if you mean you want to redirect from the index action on a controller simply do:
public ActionResult Index() {
return RedirectToAction("whateverAction", "whateverController");
}
1) To redirect to the login page / from the login page, don't use the Redirect() methods. Use FormsAuthentication.RedirectToLoginPage() and FormsAuthentication.RedirectFromLoginPage() !
2) You should just use RedirectToAction("action", "controller") in regular scenarios..
You want to redirect in side the Initialize method? Why? I don't see why would you ever want to do this, and in most cases you should review your approach imo.. If you want to do this for authentication this is DEFINITELY the wrong way (with very little chances foe an exception)
Use the [Authorize] attribute on your controller or method instead :)
UPD:
if you have some security checks in the Initialise method, and the user doesn't have access to this method, you can do a couple of things:
a)
Response.StatusCode = 403;
Response.End();
This will send the user back to the login page.
If you want to send him to a custom location, you can do something like this (cautios: pseudocode)
Response.Redirect(Url.Action("action", "controller"));
No need to specify the full url. This should be enough.
If you completely insist on the full url:
Response.Redirect(new Uri(Request.Url, Url.Action("action", "controller")).ToString());
RedirectToAction("actionName", "controllerName");
It has other overloads as well, please check up!
Also, If you are new and you are not using T4MVC, then I would recommend you to use it!
It gives you intellisence for actions,Controllers,views etc (no more magic strings)