here is the code from my cshtml file
<button type="button" class="button js-book-class" onclick="#Url.Action("BookTheClass", "MemberArea", new { ClassID = Aclass.ClassID })">Book Now</button>
and here my function inside MemberAreaController.cs
[HttpPost]
public ContentResult BookTheClass(int ClassID)
{
Class selectedClass= _context.classes.Find(ClassID);
selectedClass.spaceUsed++;
_context.SaveChanges();
return Content(ClassID.ToString());//just for testing
}
This is because you have conflict.
Url.Action generates a href link, which in click, will execute a browser navigation, which means GET request.
While your server BookTheClass action is expected to be called when executing a POST.
The easy fix which in this case is also the not so good solution, is to change your method to [HttpGet] (which i believe is the default) and it will be resolved.
The more accurate solution, will be to create a form element and making the button submit the data, this will acquire you a change only in HTML and not server side.
The reason i think you should stick to Post, is because i believe in the notion that GET should be consider as a query like, and that identical requests should have the same response.
Whereas Post should have the role of Actions which result in side-effects. which in Rest, means Creation. But as general rule of changing a state in the server as a result.
Related
So, I know WHY this happens but just unsure the best way to go about fixing it.
The application I am using has a ViewModel and binds/maps the properties from a Model.
This VM is then bound to the View and using the #Html.TextBox() helper.
This is an example: The actual text value from the DB is:
"Université de Montréal"
The rendered output on the HTML in the DOM is:
<input class="validate" id="EmployerName" name="EmployerName" type="text" value="Université de Montréal" />
Now, once the page has been loaded, it does an AJAX GET call by serializing the form and sending that across.
At this point, it DOES hit the controller (because I have the [ValidateRequest(false)] attribute applied to that controller action, but I then get the error:
A potentially dangerous Request.QueryString value was detected from the client (employername="Université de Montré...").
At this point, I am stuck as to what to do to fix the problem and let the request process normally.
I know by adding the in the web.config fixes it, I do not want to change it site wide.
any ideas the best thing to do to allow this request to process as normal?
Controller action:
[ValidateInput(false)]
[OutputCache(NoStore = true, Duration = 0)]
public ActionResult GetMoreData(Criteria crit)
{
...
}
So, I managed to fixed this.
When using the MVC TextBoxFor, it automatically encoded the string (which is nice).
Adding the [ValidateInput(false)] attribute to the controller action works to the point where it can call the action method. I then simply did an HtmlDecode ON SAVE. On load, I simply HtmlEncode the string.
Not the best solution if you have a lot of properties being displayed to the user but it works.
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.
So I saw this question here on SO, but it hasn't really solved this problem for me.
I have an ASP.NET MVC 3 + Razor app running on IIS5 on my dev pc, and then IIS6 for my dev web server. Everything worked great until I deployed it. I am bin deploying everything, with no problems on that front (that I can tell).
Now I am getting this Child actions are not allowed to perform redirect actions error on my page. I am not sure how to pinpoint where it is failing.
I'm using #Html.Action to pull in some drop down boxes with data:
public ActionResult Hierarchy()
{
List<Store> storeList = DBService.getStores();
if (DBService.Error != null)
return RedirectToError(DBService.Error);
List<Department> deptList = DBService.getDepts();
if (DBService.Error != null)
return RedirectToError(DBService.Error);
VM_Hierarchy hierarchy = new VM_Hierarchy(storeList, deptList);
return View(hierarchy);
}
If I remove the #Html.Action line, the page will render. It will then break if I do an AJAX request to a controller action such as this:
[HttpPost]
public ActionResult InventoryList(string fromDate, string toDate)
{
List<Inventory> inventories = DBService.getInventories(fromDate, toDate);
if (DBService.Error != null)
return RedirectToAction("Error");
return View(inventories);
}
If this isn't correct, how am I supposed to redirect to an error page or dictate what view gets returned on a post? Any help is appreciated. Thanks.
It's probably because you're getting errors from your DB service when it's deployed, which you aren't getting locally. This is causing it to try and redirect from a child action, which you can't do.
You might want to try getting the drop-down data in your main action, and put it all in a ViewModel, so you aren't using child actions. If this fails, you can redirect to your heart's content. For the AJAX, you'll need to handle the errors on the client and do something sensible, you can't just return a redirection from an AJAX call.
This question has some further info:
Why are Redirect Results not allowed in Child Actions in Asp.net MVC 2
if you put in the child action
(ControllerContext.ParentActionViewContext.Controller as System.Web.Mvc.Controller).Response.Redirect(Url.Action("Action", "Controller")); it will redirect from the childaction.
I had a similar problem but for the life of me, I couldn't figure out where the redirection was coming from. Once I figured it out, I thought I'd post an answer in case it happens to anyone else.
If the source of the redirection is not apparent from the imperative code, e.g.:
[AuthoriseThis]
public ActionResult Details(SomethingModel something)
{
return PartialView(something);
}
Look at the declarative code!
In my case, it was an authorisation problem, so the attribute was causing the redirection, and my eyes were filtering out the existence of the attribute.
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)
I have a number of create and delete partial views that I want to reuse by calling from other views. The issue is that this requires me to pass the return route and routeValues to the create and delete controller methods so that they can generate an appropriate redirect back to the original view on success. I created some extensions and helpers to keep this tidy but it seems convoluted to approach the problem this way. Have I missed something? Is there a simple way to RedirectToAction when the (redirect) controller, action and routeValues can vary?
Example for clarity: both the Product A-Z Index View and the Product SomeCategory Index View have a delete button that calls the Delete View (which displays a "do you really wanna delete" message) which has a "Really Delete" button that posts back to the actual (POST) Delete method in the product controller. Once the product is deleted we need to return a RedirectToAction but since both the 'A-Z Index' and the 'SomeCategory Index' Views have a Delete link we have to dynamically set the action, controller and routeValues to whatever view called the delete initially.
This isn't difficult but it's extremely convoluted to pass the redirect values around all the controllers and views that handle the delete and it stands to reason there must be a saner way to do this.
Interrogate the Request.UrlReferrer in the Delete action ( the one that displays the confirmation view ) and store the referrer details in the temp data.
In the delete action, read the referrer details back out of the temp data and use the Redirect( string ) overload to redirect to the url that referred the user to the original delete request.
did you consider using RedirectToRoute
RedirectToRoute(new {controller = "MyController", Action = "Create", id = ""});
Consider not using a whole view for the 'delete confirm'. Use a Html helper and a javascript 'confirm()'. ie. render the post form and delete link with a helper so that when the user clicks "delete" they get a js confirm prompting "sure to delete?" and on ok the function "returns true" and invokes the submit on the form to delete. then the delete action simply redirects to wherever it normally would. i'd hope you are using different delete actions for the different objects you are trying to delete. if your plan is to have a generic delete action, well that's harder (and not recommended IMO).
My delete helper includes lots of things, but the delete part looks like this (with snips):
string deleteLink = String.Format(#"<a onclick=""deleteRecord({0})"" href='#'>Delete</a><form id='deleteForm' method='post' action='" +
routeRelativePath + "/" + actionPrefix + "Delete/" + model.ID +
#"'></form>", model.ID);
..and it (the helper) attaches some js too:
function deleteRecord(recordId) {
if(confirm('Are you sure you want to delete this {friendlyModelName}?\nNOTE: There is no Undo.')) {
// Perform delete
var action = "{routeRelativePath}/{actionPrefix}Delete/" + recordId;
// jQuery non-AJAX POST version
$("form#deleteForm").submit();
}
}
..you can see that the helper creates the Delete link with all the params for the route and ID etc. The js simply does the 'confirm' part then submits the tiny you can see is created by the helper.
[sorry if the samples are not 100% complete - i've had to remove lots of things: eg the helper and attached js have many different modes so as to support ajax POSTs etc]