Web API and Navigation Framework

Oct 30, 2013 at 6:17 PM
I introduced Navigation framework in our ASP .NET web application few months back. Thanks for providing a great project to abstract navigation information out of pages.

We have started adding new features into the web application. The new features in the web application are now leveraging a combination of Javascript and Web APIs. I am populating a grid (jQuery DataTables plug-in) from Web API. One column in the grid contains hyperlink and data. The target for hyperlink is a legacy web form. My question is related to accessing StateController from Web API or Javascript, so that I can construct URL for hyperlink dynamically. Here is description of my futile efforts:
  • Create URL for hyperlink in Web API controller.
    Issue: StateController is not accessible in Web API (or rather it is not initialized.)
  • Store URL "template" generated from StateController API in a hidden field. Javascript will access hidden field value and replace "placeholders" with correct value while populating grid.
    Issue: I am able to generate correct hyperlink. However, Navigation framework throws exception on clicking hyperlink due to invalid checksum. If possible, I do no want to switch off checksum in Navigation framework.
Do you recommend any elegant solution than above hacks?

Thanks,
Coordinator
Oct 30, 2013 at 9:13 PM
Edited Oct 30, 2013 at 9:13 PM
It's great to hear you're using the Navigation framework, and I'm so pleased you're enjoying it.

Thanks for getting in touch with such an interesting question. I applaud your attempts to get Navigation working inside Web API, but no hacks are needed because you can use the StateController directly.

Let's take the following StateInfo for example.
<dialog key="Person" initial="Listing" path="~/Listing.aspx">
    <state key="Listing" page="~/Listing.aspx">
        <transition key="Select" to="Details"/>
    </state>
    <state key="Details" page="~/Details.aspx">
    </state>
</dialog>
Inside Web API, if you want to get a URL to go to the Listing.aspx you can pass the Person dialog key
string url = StateController.GetNavigationLink("Person");
Things get a bit trickier if you want a URL to go to the Details.aspx. This time you first need to Navigate to the Person dialog before passing the Select transition key
StateController.Navigate("Person");
string url = StateController.GetNavigationLink("Select");
But wait! This doesn't quite work, because the first Navigation tries to do a Response.Redirect which Web API doesn't like. We can override this behaviour by passing in a NavigationMode parameter
StateController.Navigate("Person", NavigationMode.Mock);
string url = StateController.GetNavigationLink("Select");
This works because a Mock NavigationMode does a pretend Navigation, moving to the next State without getting Web Forms involved.

Let me know if this helps you out of your tricky situation.
Marked as answer by rah_mehta on 11/5/2013 at 5:57 AM
Nov 4, 2013 at 5:34 PM
Hi Graham,

Thank you for an elegant solution. This is very creative. I was tied up with other issues so I couldn't try your solution earlier. My StateInfo looks very similar to yours mentioned in your response. The only difference is that I am using route="Listing/{id}" in "Details" state. I was able to generate URL for "Details" hyperlink based on example. However, when I click on hyperlink, I get following error.

Exception Type: Navigation.UrlException, Navigation, Version=1.7.0.0, Culture=neutral, PublicKeyToken=bb3239226a232bfb
Message: The Url is invalid
Stack Trace: at Navigation.ChecksumNavigationShield.Decode(NameValueCollection data, Boolean historyPoint)
   at Navigation.StateContext.ShieldDecode(NameValueCollection coll, Boolean historyPoint)
   at Navigation.StateAdapter.Page_PreInit(Object sender, EventArgs e)
   at System.Web.UI.Page.OnPreInit(EventArgs e)
   at System.Web.UI.Page.PerformPreInit()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
I am just setting value of href attribute in Javascript based on URL generated in Web API. It seems like I am overlooking very simple setting but I am not able to put my finger on it. Do you see any missing configuration setting?

Thanks,
Coordinator
Nov 4, 2013 at 5:54 PM
Is the Web Api in the same project as the Web Forms? If not, you must make sure the same Navigation configuration is the same in both web.config files. It looks to me like the NavigationShield configuration is missing from the Web Api project's web.config?

However, if they are in the same project then I'm not sure where the problem lies. Could you show me the problem URL generated by the Web Api and also the equivalent one generated from within the Web Forms application?

Thanks,
Graham
Nov 5, 2013 at 12:57 PM
Web API is in same project. I was able to resolve this issue. The only change was from route="Listing/{id}" to route="Listing/{wcn}" in StateInfo. URL generated from route="Listing/{id}" was not working in Web Forms either. I have a route defined in my Global.asax.cs for Web API. I am not sure Web API route causes any conflict. But I am going to investigate later this week.

Thank you very much for your help. I appreciate it. Your solution will be very helpful as we continue to add new features to our application.
Coordinator
Nov 5, 2013 at 4:30 PM
I'm always happy to help and I'm glad you've got it working.

It does sound like you might have defined clashing routes, but also make sure you pass any route parameters in NavigationData
StateController.Navigate("Person", NavigationMode.Mock);
string url = StateController.GetNavigationLink("Select", new NavigationData() { { "wcn", 1 } });
Nov 7, 2013 at 2:03 AM
FYI. I am passing route parameters in NavigationData.