I suspect I'm doing this wrong.
For various reasons, my app forces the user to make some choices right after login. In order to ensure that they enter the necessary data, I override the OnActionExecuting method in a base controller class to intercept any attempt at executing an action before this data has been entered, and redirect the user to the necessary page. I preserve the url of the action they were attempting to execute with the following code:
url = Url.RouteUrl("Default", filterContext.RouteData.Values);
(filterContext is an ActionExecutingContext object, and a parameter of OnActionExecuting.)
The problem I'm having is that, if the action was associated with a controller in an area, the url I get doesn't reflect the area.
I understand from other posts that I can get the area name from the DataTokens collection of RouteData. But I'm uncertain of the best way to pass it. I suppose I could retrieve it and use the RouteValueDictionary.Add method to add it to RouteData.Values (assuming Values is not read-only at that point; I don't know), but that feels a bit ... odd, like somehow the point is being missed.
Is this really the way this is supposed to be done? Is there something wrong elsewhere, that Area is absent from my RouteData.Values?
I would just take it out of the data tokens in the filter and add it to route values. You can do it with RouteValues.Add:
if (filterContext.RouteData.DataTokens.ContainsKey("area"))
filterContext.RouteData.Values.Add("area",
filterContext.RouteData.DataTokens["area"]);
The areas feature was added in MVC2, and I imagine this is a side effect of it not being in MVC1. However, as long as your RouteValues contains an "area" key, UrlHelper.RouteUrl should generate the correct URL for the area.
Related
The specific case I've got in mind is as follows: an AjaxFormComponentUpdatingBehavior("onchange") is added to a TextField in a form. The behavior verifies the text for certain conditions (either the model object or the form component model, doesn't matter), based on which it might display a message (or hide it, if it has already been shown).
The problem is, there are also validators added to the TextField. One of the possible (and likely) scenarios consists of the user typing in, first, a value that causes the message to be displayed by the AJAX request. If, then, he/she types in a value that doesn't pass validation, the message should disappear, but it does not.
Apparently, either the onUpdate() method for the AJAX behavior is not called at all, or I am failing in my attempts to insert a check for non-validated entries (I have tried to test for both null values and empty strings, to no avail; I have no idea what exactly Wicket's validators do to models when data is invalid).
I am wondering if someone who actually understands validators (or AJAX, actually) has any ideas on where the problem could be.
I can post edit and post code if someone tells me this is not a general issue tying validators and AJAX, but most likely a programming mistake. I still believe the former and thus I'll refrain from posting code sections, in order to keep the discussion on an API/theoretical frame.
Thanks.
When using an AjaxFormComponentUpdatingBehavior, if any of the IValidators fail their validation, onError() will be called instead of onUpdate(). Wicket will effectively prevent invalid user input from reaching the IModels in your components, so the component's ModelObject will not be changed at all. The invalid input will probably remain available by means of getInput()/getConvertedInput() (not sure if it will in an AJAX scenario, it sure is in a traditional form submission).
However, take into account that IFormValidators are not executed when using this mechanism. If you've got any, you might be interested in overriding getUpdateModel() so that AjaxFormComponentUpdatingBehavior will not bring maybe-invalid user input into your IModels, and set modelobjects manually when you're certain user input is valid.
Regarding your specific case, you could perform all the required logic in onError() (or rely on Models that will grab data from somewhere else), and just add the components that need refreshing to the AjaxRequestTarget. This is probably what's missing in your scenario.
Is it possible to set the absoluteURI in the controller after clicking on an action link? So for example:
User clicks on a link called "GoHere". The current URL is domain.com/section/place. When the link hits the method in the controller, it recognizes that the user is currently in a section called "section", even though in the file structure section doesn't exist. The link itself actually points to domain.com/place2. Instead of returning a URL of domain.com/place2, it returns domain.com/section/place2.
The reason I ask is because for what I'm doing, the section is completely arbitrary and doesn't exist. It's just there to give the impression that the user is in another section. I know I could create extra sets of controllers, but I'm trying to get around this since for management purposes it's better if I just have one set of controllers. Is this possible? Thanks.
In your gobal.asax, try setting your route to require section for the control. Maybe "{control}/section/{action}/" and whatever else you need.
I have a GET action for creating records. Because the page is somewhat dynamic, I don't use a model to hold the data. I go off to do some OAuth, only to return to the create screen later on. In order to pass the data back, I am redirecting with a query string. I parse the query string in the GET action, and then show the view. The thing is, the query string is showing up in the browser. This displays pseudo-sensitive data.
Since I am only using the query string for transferring data, I am wondering if I can throw the query string away to prevent it from showing up on the browser.
Otherwise, is there a way to go to another action without redirecting? I've found, if I call the "other" action method directly, it tries to find the view of the original action. I can explicitly change the return View(viewModel) line to return View("create", viewModel) but that seems really dirty.
You should consider changing the action to accept POST requests. At least this will prevent the sensitive information from appearing in the browser. For extra security, your site should be served via SSL.
The other thing you can try is encrypting the sensitive values or the entire query string. The only problem is that this, too, will be preserved in the browser's history unless you require users to log in.
It looks like your action method is trying to do too much. Authentication/authorization is a separate concern which should not be part of the action method. It is better to move the authentication work in to an action filter.
Create an class that extends authorization attribute and override its OnAuthorization method to do your authorization work.
This frees your controller action method to accept POST requests.
I know that we can design the layout in *.xml then in the action just invoke loadLayout, and renderLayout to render the blocks/views.
But, I have a question is:
- How can I load the layout at runtime?
If we have an action which does not really design its layout and will be decided how to render at runtime.
You can please consider the answer from the question for more clear.
Writing a new answer because it seems that you actually DO still want to render, you just want to render a different route's layout XML updates. I believe the _forward() method from Mage_Core_Controller_Varien_Action will allow you to do what you are describing with the least amount of pain.
You should add your action controller directory ahead of the catalog directory, create a ProductController with a viewAction, and check customer is not logged in - in this check you would call $this->_forward('customer','account','login');.
This approach though is going to require more effort in order to be usable, as I imagine that you want the user to be sent to the product page upon login. Have you seen Vinai Kopp's Login Only Catalog module? It should do this for you.
loadLayout() and renderLayout() just execute block output method toHtml() (usually) and take the resulting strings and apply them to the response object via appendBody(). In an action controller you can just call $this->getResponse()->setBody('response string'). How you build the string is up to you.
You can also use Mage_Core_Block_Flush to immediately send output to the browser without using the response object.
Is it possible to prevent direct access to an action in symfony. The action is only accessible by "forward" only. So basically, a way to see if the request is coming from another action.
I'm trying to achieve this because the first action handles plenty of verifications then if it fails, it stays on that action. If it succeed, it will forward to an appropriate action; this action needs to have safe inputs (validated from the first action). In order to keep the code DRY, the second action doesn't need to re-verify all the inputs again.
Then why not doing simply a private method? The second action is sort of a plugin, it's decided on the fly where it's going from the first one, that action has its own set of other future action/template. It makes more sense to simply forward instead of trying to handle plenty of cases that Symfony already takes care of.
There are multiple ways to achieve this.
1) Make sure your action isn't accessible by the routing. If you have wildcard routes this will be harder, but you can always add a route which would point the url for your action to a 404 page. Add something like this to your routing.yml:
disabled_action:
url: /disabledController/disabledAction
params: { module: default, action: error404 }
2) Check the action stack upon executing your action. The action stack let's you know from which action you were redirected. You can access it within your action using $this->getController()->getActionStack(). If the getSize() is bigger than 1 (in a default configuration) you we're forwarded.
Use referrer parameter available in request
$request->getReferer() will give you full url of previous action
I'm curious why you're trying to achieve this. Are you looking to have multiple points of access that forward to this action? What if you simply defined a private method (which by default aren't web-accessible) and called it directly from another action?