-
Notifications
You must be signed in to change notification settings - Fork 218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
parameters for current request injected into nodes where the action has the same parameter name #213
Comments
Hmm, when you posted your original question on StackOverflow, I assumed you were talking about the node matching behavior. Now it sounds more like you are talking about the URL resolver. The URL resolver gets the route values from the current node, not the current request. And by default the values are not copied from the current request to the node (not even when using action method parameters). There are 3 ways to configure MvcSiteMapProvider to copy values from the current request to the current node:
I would suggest to check to ensure you don't have any SiteMapPreserveRouteData attributes declared on your action methods, as that is a brute force way to copy the values from the current request to all of the nodes. Also, double check whether you are doing one of the other 2 methods for preserving route parameters from the current request. Honestly, I am still learning how the node matching features work and so far I haven't thought of a single use case where SiteMapPreserveRouteData would actually be useful. Totally anomalous is a good description of the way it behaves. In fact, when I was refactoring I suggested leaving it out of the new version. The best way I have found to configure MvcSiteMapProvider is to ensure that there is a unique node for every one of the possible action method parameter values (or combinations of values). This not only makes the matching work right, but it ensures you don't have to worry about the action parameters being "forgotten" between requests, and also ensures that all of your pages will be included in the All nodes can be added from an external data source through the use of Dynamic Node Providers, and if the number of nodes gets out of hand, you can even extend the cache to write to the file system. There are few reasons not to take this road. AreasI also didn't notice the first time around that you are using areas. The documentation hasn't been updated yet to reflect this, but there was only one configuration I could find where areas would work right. Have a look at this answer to ensure your areas (and routes for areas) are defined correctly. Your CaseI am not ruling out that there is a bug just yet, but let's make sure your configuration is set up right before going down that road. If you check the above and are still having issues, please build a small demo project that exhibits the anomalous behavior and either post it on GitHub or zip it and make it available for download. Discovering where the problem lies is extremely difficult without working with the exact same configuration. Not to mention, sometimes the act of building a demo can reveal problems with the configuration. |
Update In v4.3.0, SiteMapPreserveRouteDataAttribute has been deprecated. I have also added a post to my blog (with working demos) that goes into detail about the node matching process and how and when to use preservedRouteParameters. If you get a chance, I would appreciate you having another look at your configuration. What you describe is happening doesn't sound like the default behavior. But if you can reproduce it and build a demo project, I would be happy to take a look at it to try to determine if there is a bug or it is misconfigured in some way (and add an exception so the misconfiguration is no longer allowed). |
I will do that, however, I am travelling at the moment, for the next month. Andrew Wrigley From: NightOwl888 [email protected] Update |
I just upgraded from v3 to v4 and I think I have the same problem. I put together a small test site at https://github.com/tomascassidy/MvcSiteMapProviderTest that demonstrates this. E.g. if I navigate to http://localhost:49429/Home/A on the test site then the |
I did another experiment here: https://github.com/NightOwl888/MvcSiteMapTest, removing MvcSiteMapProvider from the project altogether. The behavior you mentioned still exists. If this is a bug (and I am not quite certain it is), it is happening because of some behavior in MVC, not MvcSiteMapProvider. I suggest you report this to the MVC team (and darn quickly, since MVC 5 is just around the corner from being released). |
Reported to the MVC developers at https://aspnetwebstack.codeplex.com/workitem/1346 |
Looks like my bug test case is actually a "feature" in MVC. See the reply on the workitem at https://aspnetwebstack.codeplex.com/workitem/1346 and the SO answer for https://aspnetwebstack.codeplex.com/workitem/1346. I still think there's something to fix in MvcSiteMapProvider v4 as I see this behaviour happening when I generate a menu using I didn't see this happening with MvcSiteMapProvider v3 because I was doing my own DIY menu based off the System.Web.SiteMap class. I would rather use/customise the built-in DisplayTemplates that come with MvcSiteMapProvider than have to create my own menu templates to fix this. |
I just tried again to upgrade from MvcSiteMapProvider v3 to v4 without success, as this problem is still occurring. I tried to look at the MvcSiteMapProvider source code to see where it was generating the Urls but just got hopelessly lost 😕 I also tried adding "id" to the "MvcSiteMapProvider_AttributesToIgnore" app setting in web.config without success. I can't use any of the workarounds specified in the MVC bug report, because I don't see any way to control how MvcSiteMapProvider generates the Urls internally for SiteMapNodeModel or the other template helpers. |
The URLs are generated in the SiteMapNodeUrlResolver class. Just like the rest of MVC, we use the UrlHelper class to generate the URLs. By default the URLs are generated at the time the SiteMap is constructed and then stored in the shared cache. If you have user session values in your routes, then you can disable this caching by setting "cacheResolvedUrl" to "false" at the node level or by setting "MvcSiteMapProvider_EnableResolvedUrlCaching" to "false" in web.config/appSettings. The net effect of disabling this caching is that the URLs will be resolved on demand (much like the rest of MVC). I did a lot of experimenting with MvcSiteMapProvider and found 2 ways to configure it that make sense.
I went into some depth on my findings at How to Make MvcSiteMapProvider Remember a User's Position. If you are using a configuration that is unusual, you may need to tweak your configuration to get it to work (or it may simply not be supported). It might help if you post your routes, nodes, and appSettings configuration here to see if we can spot the problem. The "MvcSiteMapProvider_AttributesToIgnore" setting is to specify custom attributes without having them automatically added to the RouteValues dictionary. Typically, you would not want to exclude your "id" parameter from RouteValues. |
I read that blog post and now I'm thinking that I am "doing it wrong", but I'm not quite sure what is the correct way to achieve this. I've updated the test project at https://github.com/tomascassidy/MvcSiteMapProviderTest to show how my project is structured. There are a few Actions/Views that are accessed with an optional Id parameter when clicking an in-page link from another view, but are accessed without the Id parameter from the main menu (the one generated by the HTML Helpers/display templates). Eg. If I access /Contact/Contact via the auto-generated menu, then all is good and the menu links point to the correct action methods. But if I access /Contact/Contact/4 via the link on /About/About2 then all the menu links pointing to action methods with optional Id parameter will also contain the value 4 in their URLs. My question is: how do I tell MvcSiteMapProvider's @Html.MvcSiteMap() helper to ignore the route values when generating the URLs? Does my problem exist because I'm using nullable int (optional) parameters for some of the action methods? |
I was able to achieve what (I think) you are aiming for by explicitly setting the id attribute to empty string on the node without any other changes to your demo project. <mvcSiteMapNode title="About2"
controller="About"
action="About2"
id=""/> This will make the menu ignore any "id" that is in the current request. So when I am on the Using I imagine if you want to, you can also set the id explicitly to empty string (or perhaps null?) on the |
Thanks for those tips. I always thought that using I got it working by keeping the |
That will work for this case, but keep in mind that if you do that you won't be able to use any custom parameters in any node. Seems quite limited if you ask me. The reason why you are getting a null reference exception is because your node doesn't match your request route.
This is not a match. Adding
But doing that gives it a side effect that if the id changes to something you don't want (for example the id of a different action method), it will copy the wrong value. So the solution is to force a non-match in every case except the default one by specifying
Not a match.
Is a match. This will satisfy the optional id parameter in every case and also only match the default route. Keep in mind the matching infrastructure of MvcSiteMapProvider is completely separate from how MVC matches a controller action. |
I reread your original post and original question on SO, and if I understand correctly the behavior you are describing is the same as what @tomascassidy has described. If so, this is a "feature" of MVC which (if you are following along) is something that they don't intend to fix. It is by design that the URL generation will pick up ambient values to attempt to match them when generating the URLs. Their advice about generating URLs appropriately is the following:
I have described how to accomplish item 2 above with MvcSiteMapProvider, by specifying the value explicitly. While I agree that this behavior seems anomalous, I feel we have done our part to successfully replicate what MVC does, which is what we are aiming for. Although we have provided ISiteMapNodeUrlResolver as an extensibility point where you can override this behavior, according to this post it is not possible to override the URL generation (UrlHelper) of MVC. So without further ado, I am going to mark this issue closed. Feel free to reopen it if I misunderstood your OP and this is a different issue entirely. |
This turns out to be a bigger issue than I first thought because of the resolved URL caching. Because MVC grabs "ambient" values from the user's request, caching these URLs has the side effect of making one user request affect the other users. To reproduce this problem, run a project that is configured to use MvcSiteMapProvider with the default route and then modify the Mvc.sitemap file to invalidate the cache. Manually type A solution for this issue is already in the works - to make a fake HTTP context that has no route values or query string and using it with the UrlHelper to change the result. |
…onsidered when resolving URLs (maartenba#213).
…f ISiteMapNode and provided support for populating this property through XML, Dynamic Nodes, and MvcSiteMapNodeAttribute.
…onsidered when resolving URLs (maartenba#213).
I have found that if an action has one or more parameters with the same name(s) as one or more of the parameters of the current request, then the value of the current request parameters are used in the method, unless the configuration of the node EXPLICITLY uses a different value.
This is an anomalous and unexpected behaviour. There is no reason for the values of the current request parameters to have any relevance to any node other than the current node.
For example, if the current request calls an action with an order and a size parameter, with respective values of 7 and 204, then the menu item for another node where the action ALSO has parameters order and size AND the configuration of the node DOES NOT specifiy values for these parameters, then the rendered menu item will use the values of order and size of 7 and 204 respectively.
Ie:
Current node url:
mysite.com/SolarSystem/Mercury/7/204
Other node for an action with order and size parameters (that are unspecified in the configuration of the node) will render as:
mysite.com/LocalCluster/MilkyWay/7/204
This seems to me WRONG and totally anomalous.
The correct url for the "other node" would be:
mysite.com/LocalCluster/MilkyWay
There is no reason for the other node to render the values for the current request.
I believe that the correct way would be to ignore the parameters and ONLY use the values EXPLICITLY stated in the configuration of the node. The current situation is that, UNLESS the values are explicitly stated, then the values of the current request are used.
Has to be wrong.
Seems to me, anyway.
The text was updated successfully, but these errors were encountered: