Html.BeginForm in WebApi - routing - asp.net

I have a Web API project as part of my solution (also containing an MVC4 project) and within the Api project I am trying to post a form to the Values controller Post method (from a view also within the Api project).
Using Html.BeginForm() or Html.BeginForm("Post", "Values") posts to /Values/Post but I need it to go to /api/Values/Post
Any idea which overload or settings I need to post to the correct location?
I can hit all the action methods fine from fiddler (e.g. localhost/api/values).

You would need to use BeginRouteForm as link generation to Web API routes always depends on the route name. Also make sure to supply the route value called httproute as below.
#using (Html.BeginRouteForm("DefaultApi", new { controller="Values", httproute="true" }))

The API controller uses a different route to the default. It's supposed to be consumed from JS (AJAX) rather than a real form post so there's no obvious support for it in HtmlHelpers. Try:
Html.BeginForm("values", "api")
This would trick it into thinking "values" is the action and "api" is the controller. "Post" is inferred from the http method.

Related

Symfony redirect to dynamic route name

I'm using the Symfony CMF Routing Bundle to create dynamic routes (I'm using one example here):
$route = new Route('/dynamic-url');
$route->setMethods("GET");
$route->setDefault('_controller', 'AppBundle:MyRoute:getResponse');
$routeCollection->add('my-dynamic-route', $route);
The response is loaded from the getResponseAction() function inside the MyRouteController:
/**
* No annotations here, because I want the url to be dynamic from the database
*/
public function getResponseAction(Request $request) {
return $this->render('dynamic-page-template.html.twig');
}
When I go to '/dynamic-url', it works.
When in another controller, I want to redirect to this dynamic route, like this:
return $this->redirectToRoute('my-dynamic-route');
But I get this error: "None of the chained routers were able to generate route: Route 'my-dynamic-route' not found"
Also interesting: when I go to '/dynamic-url', the dev bar actually says that the Route name is 'my-dynamic-route'.
Edit
When I load all the routes, I don't see my dynamic route names:
$this->get('router')->getRouteCollection();
I think they should be in this list.
Since it's a dynamic route, which wasn't saved anywhere (like routing.yml ) it will be only availabe for Request where it has been defined. So at the end of Request your app will immediately "forget" about new Route generated at runtime.
When I load all the routes, I don't see my dynamic route names:
$this->get('router')->getRouteCollection();
I think they should be in this list.
Actualy No. It depends on where you call $this->get('router')->getRouteCollection();
Just try to call
dump($this->get('router')->getRouteCollection();)
right before the return statement in your Action where you're adding the my-dynamic-route route. I'm sure you'll see your my-dynamic-route in the list of routes, but if you call it anywhere else - you won't see it.
It's less about symfony rather about stateless nature of web (see Why say that HTTP is a stateless protocol?)
I started to think about this and pointed your question to an routing issue on symfony-cmf. You tagged with #symfony-cmf and i think this would be important feature for us.
I also think, when you persist your route with /my-route you should also ask the router for that name (or in case of the CMF with an content object with that a route.)
If you use the CmfRoutingBundle dynamic router, you should persist your routes to doctrine. The idea of dynamic here is that they can be created at runtime, e.g. in an admin interface or elsewhere by code. The DynamicRouter loads routes from the database.
If you use PHPCR-ODM, the route name is the repository id, typically something like /cms/routes/my-route and you can generate it with that name. If you have access to the route object loaded from the database, you can also generate from that, rather than the path. If you have to hardcode a path in your application, its an indication that probably a normally configured route would be better.
If you just add a route to the route collection on the fly, you would have to make sure that happens in each request, its only available when you add it. With the CMF ChainRouter you could register your own router that does that, but i would not know of a good use case for that. Either you know the route, then you can configure it in the routing.xml|yml|php config file. Or routes are loaded dynamically, in which point you should use DynamicRouter, potentially with a custom route loader.

Difference between wcf and web api uri definition

I want to convert our existing WCF REST web services to ASP.NET Web APIso I started to look into it.
Getting one of my function (i.e. login) up and running in ASP.NET Web API was quite straight forward but there is one thing I'm confused about and I hope one of you can clarify this for me.
In our WCF REST web service, our login (POST) function was called as follows:
http://localhost/mywebsite/mywebservice.svc/Authentication/Login
We'd pass a LoginRequest to it and we'd get a LoginResponse back.
Now in ASP.NET Web API, I've our Login (POST) function is being called as follows:
http://localhost/api/authentication and I'm passing the same LoginRequest and I get the same LoginResponse.
My confusion is, how does ASP.NET Web API know to use the Login function which is defined in the AuthenticationController?
I assume it has something to do with the parameter type being passed but what if I have another function that has the same parameter type, how would it differentiate between the 2?
For example, what if I had a LocalLogin and CloudLogin (not the case btw) and both require the LoginRequest as an input parameter and both return the LoginResponse, how would it know which one to call since it's not part of the URI?
Thanks.

Html.RouteLink to a Web API Route - possible?

My site is largely a suite of web services exposed via the Asp.Net Web API. There are also pages, designed to support the webservices (testing etc), written in Razor (and implicitly Asp.Net MVC 4).
For the XML versions of the webservices I have a schema-export action (uses the XsdDataContractExporter) which is picked up by my standard API route (although note - I've flipped the precedence of the Web API and Pages):
//page routes
routes.MapRoute(
"Default", // Route name
"pages/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional
} // Parameter defaults
);
//an additional route for my Schema controller action
routes.MapHttpRoute("XSD", "schema.xsd",
new { controller = "schema" });
//API Catch-all Route
routes.MapHttpRoute("APIMain", "{controller}/{id}",
new { id = RouteParameter.Optional });
Now on a razor page I want to emit a link to the 'friendly' schema URL ~/Schema.xsd. Anticipating issues with route discovery I immediately went for hitting the route directly by name:
#Html.RouteLink("Schema", "XSD");
However this just emits a link equivalent to ~/.
I've tried some other combinations of route values - but it appears MVC's HtmlHelper and UrlHelper simply don't want to pick up Web API routes.
I'm sure if I cracked open the source of Asp.Net MVC 4 I'd find the reason - but I'm hoping somebody already knows, and since I can't find another SO about such cross-linking I figured it'd be a good addition to the SO library.
I should add that browsing to ~/Schema and ~/Schema.xsd do correctly display the XML schema produced by the API action.
Update
Post-RC a method was added to MVC's UrlHelper, HttpRouteUrl, which does exactly the same thing I suggest here in this answer. This is my discussion thread over on CodePlex where I was told this. So there is no need for you to use the magic string mention here in generating links to Web API routes.
Original answer
I've managed to get it to work - although it might not by the time MVC 4 is RTMd (disclaimer disclaimer!)
I changed my Html.RouteLink call as follows:
#Html.RouteLink("XML request schema", "XSD", new { httproute = true })
I didn't originally intend to answer my own question straight away - but having done some research I found an answer.
First I verified that the HtmlHelper's route collection is the same as the RouteTable.Routes collection (i.e. contained all routes).
Following the call-chain through, I remembered having trawled through the current Web API and page MVC 4 source code from CodePlex, that HttpRoutes (in System.Web.Http.Routing) need a 'hidden' route value to be added otherwise they will never match. Here's the source code from lines 21-25 of HttpRoute class (correct as of 8th June 2012 source):
/// <summary>
/// Key used to signify that a route URL generation request should include HTTP routes (e.g. Web API).
/// If this key is not specified then no HTTP routes will match.
/// </summary>
internal const string HttpRouteKey = "httproute";
A bit of further analysis of the code showed that it expects this route value to be a boolean.
Clearly, this is something that can be turned into extension methods - perhaps Html.HttpRouteLink (and Html.HttpActionLink) - with extra extensions on UrlHelper for hiding the magic string for the route data value.

How Do I Get RouteData Values from a Web Service in .Net 4.0

I am trying to extract an id number from a URL using a web service so that it can be used as a parameter for a where clause in a select statement that produces data from a database based on the id number of a record. That data will then be passed back to the page to populate an element in a jQuery modal popup widow.
Everything works fine with a static id number (ex: string postid = "120"), but I don't know how to get the id number from the URL. I'm using Routing in .Net 4 and the method for accessing Routing in pages does not work in a web service. In pages I just do stuff like var id = RouteData.Values["id"]; and that gets the id, but when i did it in a web service I got an error:
CS0120: An object reference is required for the non-static field,
method, or property 'System.Web.Routing.RouteData.Values.get'
Summary:
I have web service accessed form a details page where I want to get RouteData for the page making the request. I want to do this just as easily as I can on a page using RouteData.Values which is just as easy as the now the obsolete Request.Querystring.
Now I am more confused because although I could easily add a new route for the web service I don't know I would call that using jQuery Ajax because of the webservice.asmx/webmethod syntax.
Right now I have URL: "../webservices/googlemaps.asmx/GetGoogleMap" in my jQuery Ajax, but that is not a real URL. It only exists in jQuery somewhere and the way to call the service using just JavaScript is no a real URL either, its webservice.webmethod() which in this case would be googlemaps.GetGoogleMap().
I will try registering a route for webservices/googlemaps.asmx/GetGoogleMap/postid, but I doubt it will work because GetGoogleMap is not a directory or a querystring.
Get current http request and use RequestContext property to get request context - it has current routing data. For example,
var id = HttpContext.Current.Request.RequestContext.RouteData.Values["id"];
In case of WCF based web service, make sure that service is participating in ASP.NET pipeline (see ASP.NET Compatibility)
EDIT: Sorry for misleading answer - the above will not work unless web service url is registered in routing engine. However, it may not solve your issue of retrieving the id - what kind of service implementation are you using? Are you making a GET request or POST request? Typically, web service handler (asmx) or WCF pipeline should convert GET/POST parameters to method parameters. Post your web service code and how you invoke it.

Asp.Net MVC routing diffrence in www.Mysite.com/Employee and www.Mysite.com/Employee/ while using JqGrid

I am using ASP.NEt MVC for one of my project.
In this I have Employee controller which can be called by www.Mysite.com/Employee/ url.
Also I have used JqGrid which uses followng to fetch data
url: "GetGridData"
While testing the same I found that
If i type www.Mysite.com/Employee/ in browser a call is made to
www.Mysite.com/Employee/GetGridData
If i type www.Mysite.com/Employee in browser a call is made to
www.Mysite.com/GetGridData
Note: the missing / at the end in second call.
How to rectify this as the chances are end user can type any of this url in browser.
I'd take a look at how you're asking JqGrid to make it's web service call - because it won't know anything about MVC's routing engine by default - and this is all happening client side.
Stepping outside of MVC for a minute, if I have a page:
example.com/page1.aspx
And have a relative link to another page on there:
Click here
The browser will look for page2.aspx at the same level as page1.aspx, i.e.
example.com/page2.aspx
If I move page1 to a new folder:
example.com/NewFolder/page1.aspx
The browser will ask for
example.com/NewFolder/page2.aspx
when a user clicks on the link.
The same thing is happening to your GetGridData call - these are being made by the web browser to your server based on the information it has available to it.
So if your page responds on:
example.com/Employee
And asks for a relative request to:
GetGridData
The browser will send that request to the same level that Employee appears to be on:
example.com/GetGriddata
Which then fails because the routing engine can't find a route for that request.
You should look at generating the URL for the GetGridData call dynamically through the routing system, which will ensure that it's built as:
url: "/Employee/GetGridData"
Final edit to add
Forgot to mention, you should probably use the UrlHelper Action methods for this:
url: <%=Url.Action("GetGridData")%>
This will generate a path to the GetGridData method on the current controller. If you need to access a different controller, or pass some values, there are overloads to help.
Try debugging your route:
Phil Haack's: ASP.NET Routing Debugger

Resources