I have a WebAPI controller with a Get method as follows:
public class MyController : ApiController
{
public ActionResult Get(string id)
{
//do some stuff
}
}
The challenge is that I am attempting to implement WebDAV using Web API. What this means is that as a user browses down a folder structure the URL will change to something like:
/api/MyController/ParentFolder1/ChildFolder1/item1.txt
Is there a way to route that action to MyController.Get and extract out the path so that I get:
ParentFolder1/ChildFolder1/item1.txt
Thanks!
"Dynamic" route is not a problem. Simply use wildcard:
config.Routes.MapHttpRoute(
name: "NavApi",
routeTemplate: "api/my/{*id}",
defaults: new { controller = "my" }
);
This route should be added before default one.
Problem is that you want to end URL with file extension. It will be interpreted as static request to .txt file.
In IIS7+ you can work around that by adding line in web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
Don't forget that if you use MyController, then route segment is just "my"
Use the NuGet package "AttributeRouting Web API". You can specify specific routes for each action, including dynamic parameters.
I was just dealing with this so try it out, and come back if you need more help.
Related
I'm "playing" around with custom inbound URL routing and have came across a problem.
When I pass my custom route a URL to examine, that ends in *.+, my class is not fired when i submit the request.
An example URL would be "~/old/windows.html"
When I step through this in the debugger, my RouteBase implementation doesn't fire. If i edit the url that i pass to the constructor of my route to try to match against "~/old/windows", my implemetation is fired as expected.
Again, If i change the url ro examine to "~/old/windows." the problem reoccurs.
My Route Implementation is below :-
public class LegacyRoute : RouteBase
{
private string[] _urls;
public LegacyRoute(string[] targetUrls)
{
_urls = targetUrls;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (_urls.Contains(requestedURL, StringComparer.OrdinalIgnoreCase))
{
result = new RouteData(this, new MvcRouteHandler());
result.Values.Add("controller", "Legacy");
result.Values.Add("action","GetLegacyURL");
result.Values.Add("legacyURL", requestedURL);
}
return result;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
In the RoutesConfig file I have registered my route like so :-
routes.MapMvcAttributeRoutes();
routes.Add(new LegacyRoute(new[]{"~/articles/windows.html","~/old/.Net_1.0_Class_Library"}));
Can anyone point out why there is a problem?
By default, the .html extension is not handled by .NET, it is handled by IIS directly. You can override by adding the following section in Web.config under <system.webServer> -
<handlers>
<add name="HtmlFileHandler" path="*.html" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
As pointed out here. The above will route EVERY .html file request to .NET, you might want to be more specific by providing a more complete path if you don't want your routing to handle every .html file.
I've found the problem, and I'm sure this will help out a lot of fellow developers.
The problem is with IIS Express that is running via Visual Studio.
There is a module configured in the applicationhost.config called :-
UrlRoutingModule-4.0
This is how it looks in file :-
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler,runtimeVersionv4.0" />
You need to set the preCondition Parameter to "".
To do this :-
Run you app via Visual Studio
Right click on IIS Express in your system tray, select "Show All Applications"
Click on the project you wish to edit, then click the config URL.
Open the file with Visual Studio, Locate the module and ammend.
Hope this helps anyone else, who ran into a similar problem.
I would like to be able to use Spring MVC as REST server and AngularJS on client side.
I have several urls for REST :
/rest/products
/rest/products/{id}
And i have several urls for the UI :
/shop/products
/shop/products/{id}
Since it is AngularJS which do the trick on client side, i just want to be able to redirect all default ui urls (not the rest ones) to the index.html file used by AngularJS.
So, in Spring MVC configuration, i would like to be able to do something like that :
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = "com.mypackage.web")
public class WebAppConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/**").setViewName("index");
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/");
resolver.setSuffix(".html");
return resolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
With that, i want to delegate all the UI urls handling to AngularJS.
I also want that if the user writes a bad url in the browser he would be redirected by Spring MVC on the index.html file and it will be AngularJS which will do the redirect on the error ui page. I have seen several projects on the web with a single index.html file, but no one handle this error case.
I have been struggling a lot of time trying to do this trick but i can't find a solution.
So my question is : how can i do that ? and more generally, am i wrong with this Spring MVC-AngularJS wanted configuration ?
Very important : I use Spring MVC 3.2 and Tomcat 7.34 without web.xml (full Servlet 3.0)
Thanks a lot.
Maybe it is possible to solve it via $routeProvider:
$routeProvider.when('/redirect/:redirectParams', {templateUrl: 'partials/homePartial', controller: redirectController});
$routeProvider.when('/home', {templateUrl: 'partials/homePartial', controller: homeController});
$routeProvider.when('/someRoute', {templateUrl: 'partials/somePartial', controller: someController});
$routeProvider.when('/error/', {templateUrl: 'partials/errorPartial', controller: errorController});
$routeProvider.when('/error/:errorMessage', {templateUrl: 'partials/errorPartial', controller: errorController});
$routeProvider.otherwise({redirectTo: '/error'})
Just let the $routeProvider redirect to an error page when the route was not found.
Edit:
I've added a redirectController in the above example. This controller will read the $routeParams, in this case $routeParams.redirectParams and use $location to change the route to /error.
Spring just redirects to http://host:port/index.html/#/redirect/error=blabla. You could and should probably finetune this and support multiple routeParams.
In Spring you would have basically three request mappings:
REST request mapping
index.html request mapping
other urls request mapping
To redirect all other requests:
#RequestMapping(value = "{path}", method = RequestMethod.GET)
public String redirect(#PathVariable String path) {
String route = null;
if (path.equals("/") || path.startsWith("/index.html")) {
// load index.html
} else {
route = "redirect:/index.html/#/redirect/" + path;
}
return route;
}
I think you can write an Interceptor which allows some known URLs like /rest/, /resources/, /webjars/* and for any other URLs redirect to index.html.
Try using urlrewritefilter
This should do the work for you. You'll have to configure/add it in your web.xml to every url would be handled and you can manipulate it to redirect to the index.html
I know its a bit late but in case someone run into the same trouble here's the URL filter sample put in your classpath urlwrite.xml
You can set redirection of certain urls you want to filter.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN"
"http://www.tuckey.org/res/dtds/urlrewrite4.0.dtd">
<!-- Configuration file for UrlRewriteFilter http://www.tuckey.org/urlrewrite/ -->
<urlrewrite>
<rule>
<note>
The rule means that requests to /test/status/ will be redirected
to
/rewrite-status
the url will be rewritten.
</note>
<from>/index.ipxs/directory</from>
<to type="redirect">/index.ipxs/marketplace</to>
</rule>
<rule>
<from>/index.ipxs/faq</from>
<to type="redirect">/index.ipxs/marketplace</to>
</rule>
<rule>
<from>/index.ipxs/marketplace</from>
<to type="redirect">/iPlexus/index.ipxs</to>
</rule>
</urlrewrite>
I've got the following action on an ApiController:
public string Something()
{
return "value";
}
And I've configured my routes as follows:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
In the beta, this worked just fine, but I just updated to the latest Release Candidate and now I'm seeing errors on calls like this:
The requested resource does not support http method 'GET'.
Why doesn't this work anymore?
(I suppose I could get rid of {action} and just make a ton of controllers, but that feels messy.)
If you have not configured any HttpMethod on your action in controller, it is assumed to be only HttpPost in RC. In Beta, it is assumed to support all methods - GET, PUT, POST and Delete. This is a small change from beta to RC. You could easily decore more than one httpmethod on your action with [AcceptVerbs("GET", "POST")].
All above information is correct, I'd also like to point out that the [AcceptVerbs()] annotation exists in both the System.Web.Mvc and System.Web.Http namespaces.
You want to use the System.Web.Http if it's a Web API controller.
Although this isn't an answer to the OP, I had the exact same error from a completely different root cause; so in case this helps anybody else...
The problem for me was an incorrectly named method parameter which caused WebAPI to route the request unexpectedly. I have the following methods in my ProgrammesController:
[HttpGet]
public Programme GetProgrammeById(int id)
{
...
}
[HttpDelete]
public bool DeleteProgramme(int programmeId)
{
...
}
DELETE requests to .../api/programmes/3 were not getting routed to DeleteProgramme as I expected, but to GetProgrammeById, because DeleteProgramme didn't have a parameter name of id. GetProgrammeById was then of course rejecting the DELETE as it is marked as only accepting GETs.
So the fix was simple:
[HttpDelete]
public bool DeleteProgramme(int id)
{
...
}
And all is well. Silly mistake really but hard to debug.
If you are decorating your method with HttpGet, add the following using at the top of the controller:
using System.Web.Http;
If you are using System.Web.Mvc, then this problem can occur.
This is certainly a change from Beta to RC. In the example provided in the question, you now need to decorate your action with [HttpGet] or [AcceptVerbs("GET")].
This causes a problem if you want to mix verb based actions (i.e. "GetSomething", "PostSomething") with non verb based actions. If you try to use the attributes above, it will cause a conflict with any verb based action in your controller. One way to get arount that would be to define separate routes for each verb, and set the default action to the name of the verb. This approach can be used for defining child resources in your API. For example, the following code supports: "/resource/id/children" where id and children are optional.
context.Routes.MapHttpRoute(
name: "Api_Get",
routeTemplate: "{controller}/{id}/{action}",
defaults: new { id = RouteParameter.Optional, action = "Get" },
constraints: new { httpMethod = new HttpMethodConstraint("GET") }
);
context.Routes.MapHttpRoute(
name: "Api_Post",
routeTemplate: "{controller}/{id}/{action}",
defaults: new { id = RouteParameter.Optional, action = "Post" },
constraints: new { httpMethod = new HttpMethodConstraint("POST") }
);
Hopefully future versions of Web API will have better support for this scenario. There is currently an issue logged on the aspnetwebstack codeplex project, http://aspnetwebstack.codeplex.com/workitem/184. If this is something you would like to see, please vote on the issue.
Have the same Setup as OP.
One controller with many actions... less "messy" :-)
In my case i forgot the "[HttpGet]" when adding a new action.
[HttpGet]
public IEnumerable<string> TestApiCall()
{
return new string[] { "aa", "bb" };
}
Same problem as above, but vastly different root. For me, it was that I was hitting an endpoint with an https rewrite rule. Hitting it on http caused the error, worked as expected with https.
Replace the following code in this path
Path :
App_Start => WebApiConfig.cs
Code:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}/{Param}",
defaults: new { id = RouteParameter.Optional,
Param = RouteParameter.Optional }
);
I got this error when running a query without SSL.
Simply changing the URL scheme of my request from HTTP to HTTPS fixed it.
I don't know if this can be related to the OP's post but I was missing the [HttpGet] annotation and that was what was causing the error, as stated by #dinesh_ravva methods are assumed to be HttpPost by default.
My issue was as simple as having a null reference that didn't show up in the returned message, I had to debug my API to see it.
In my MVC application I want behavior as was previously specified in <httpHandlers> in web.config, namely that if I register a handler like this:
<system.webServer>
<handlers>
<add name="processData" verb="POST" path="processData" type="RightType, RightAssembly"/>
</handlers>
</system.webServer>
then all requests to /processData that have verbs other than "POST" result in HTTP 404.
I tried to register a route like this:
routes.MapRoute(
#"ProcessData", #"processData",
new { controller = #"Api", action = #"ProcessData" },
new { httpMethod = new HttpMethodConstraint( "POST" ) } );
and now once a request has a verb other than POST the route isn't matched, route resolution falls through and proceeds to the default page.
How do I make MVC produce an HTTP error message (code 404 or anything like that) once a path matches but a verb mismatches?
You could just register another route with the verbs that you don't want and make that lead to a NotFound action.
Routing works by following routes in the order specified until it finds one that matches. By adding the constraint you make this route fail and so it goes down the list to find the next matching route (your default).
To get the behaviour you want you need to have your route catch the request and then handle the error.
Take the constraint out of the route and on your controller do the following:
[HttpPost]
public ActionResult processData(myModel myPostedModel)
{
DoStuff();
Return View();
}
public ActionResult processData()
{
throw new HttpException(404);
}
I am assuming that you do model binding on your action method here because you do need differing method signatures. If not then you'll need to take off the HttpPost attribute and test for the request method there.
I can't exclude non-existing files from the routing system. I am dealing with this code in a Web Forms scenario:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{resource}.gif/{*pathInfo}");
routes.IgnoreRoute("{resource}.jpg/{*pathInfo}");
Route r = new Route("{*url}", new MyRouteHandler());
routes.Add(r);
}
When I debug
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
string path;
IHttpHandler page;
try
{
path = requestContext.RouteData.GetRequiredString("url");
LogFile(requestContext, path);
}
path still contains non existing gif files, jpg etc
I want to exclude all files that have an extension if that’s possible
Is something wrong with the code above? Is the order correct, i.e. add routes.IgnoreRoute entry prior to adding a route to RouteCollections?
In Web Forms, you can use the StopRoutingHandler. In the following example, routing will be ignored for files in the /images folder like http://yoursite.com/images/xyz.jpg
routes.Add(new Route("images/{resource}", new StopRoutingHandler()));
IgnoreRoute is an extension method of ASP.NET MVC (System.Web.Mvc) - does not work in Web Forms.
Do this:
routes.Add(new Route("{resource}.gif/{*pathInfo}", new MyIgnoreHandler()));
Map your other routes to your regular handler.
You should remove the "mvc" tag from this question.