I have a default Asp.Net route as follows:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Nothing to special in it.
And I have my super simple default action in Home Controller:
public ActionResult Index(string id)
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
I can type the URL: http://localhost:12143/Home/Index/HanselandCratel
and it works fine but when I type in
http://localhost:12143/Home/Index/Hansel&Cratel
it doesn't
I understand & has to be encoded but when I type in:
http://localhost:12143/Home/Index/Hansel%26Cratel
it still doesn't work I get this error:
A potentially dangerous Request.Path value was detected from the client (&).
I am aware of setting this in web.config:
<httpRuntime targetFramework="4.5" requestPathInvalidCharacters="" />
but I am afraid I will have to sacrifice security when I do that.
Is there any other alternative to this? Perhaps any setting in Asp.Net?
I am aware of setting this in web.config: <httpRuntime targetFramework="4.5" requestPathInvalidCharacters="" />
Do not do it, you're removing all the protection given by this request validation rule. If you want to allow & character then leave all the others in-place:
<httpRuntime requestPathInvalidCharacters="<,>,*,%,:,\,?" />
but I am afraid I will have to sacrifice security when I do that.
In this way & will be allowed in your request URLs. Be careful to properly validate all input parameters and to, eventually, escape them as required. Note that it should be done also with original rule in-place...
You may re-include also other characters but I'd suggest to do it only if required. You may also add new ones: sometimes I have text IDs as parameters (for AJAX GET requests) and even if I'm sure I won't ever build a SQL command concatenating strings...I usually add ' (and few others).
Is there any other alternative to this? Perhaps any setting in Asp.Net?
Yes, you may go back to .NET 2.0 rules but I see no reason to do it...
[ValidateInput(false)] //write this
public ActionResult Index(string id)
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
<httpRuntime targetFramework="4.5" requestPathInvalidCharacters="" />
<pages validaeRequest="false" />
Try that 1st line. May be that will work for you.
I am trying to serve some JS and CSS files that are embedded into a DLL, with a solution based on this approach here: http://weblogs.asp.net/imranbaloch/asp-net-bundling-and-minification-and-embedded-resources
so, javascript and css files are embedded and I create bundles for them.
My problems start because, having quite a few of them, I need some folder structure to keep order. So the original route
RouteTable.Routes.Insert(0,
new Route("Embedded/{file}.{extension}",
new RouteValueDictionary(new { }),
new RouteValueDictionary(new { extension = "css|js" }),
new EmbeddedResourceRouteHandler()
));
is not enough anymore, so I have changed it to this:
RouteTable.Routes.Insert(0,
new Route("Embedded/{*url}",
new RouteValueDictionary(new { }),
new EmbeddedResourceRouteHandler()
));
I also cannot use the extension part because the catch-all part has to be the last one
So now if I try to access anything that looks like a file, my route will never be used so I will just get a 404
I have tried replacing the dot with a slash or adding a slash at the end but what I'm after here is a simple solution that will allow me to map urls that look like files to actual files.
I've also searched the web and there seem to be solutions based on UrlRewrite or altering the web.config but:
- I would like not to modify the IIS settings for every application to accomodate the library
- since it's a library, I would like it to be self contained and developers that use it shouldn't care about these sort of internal issues
So, is there a solution that I can implement in my library for this?
Also worth mentioning is that the original routing had the same issue, it only worked because of
<modules runAllManagedModulesForAllRequests="true" />
in the web.config, which I don't think is a good idea for performance
When you set
<modules runAllManagedModulesForAllRequests="true" />
this enables all available modules to run against the request. Which, as you mentioned, isn't the best for performance. However, you could add only the module you actually need- in this case the UrlRoutingModule.
You could add this module like this:
<system.webServer>
<modules>
<remove name="UrlRoutingModule-4.0" />
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
</modules>
</system.webServer>
If you want an even better way (IMO) to do this, disregard the WebConfig and add it in a AppStart.cs file in your class library.
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(AppStart), "PreStart")]
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(AppStart), "Start")]
namespace EmbeddedPages
{
public static class AppStart
{
private static bool PreStartFired = false;
public static void PreStart()
{
if (!PreStartFired)
{
PreStartFired = true;
DynamicModuleUtility.RegisterModule(typeof(UrlRoutingModule));
}
}
}
}
This adds the UrlRoutingModule into the module stack, and your URL's should now properly resolve. Note: you will need to add WebActivator to your project through nuget.
Background
In HomeController.cs I have:
[HttpGet]
public GetPerson(string name)
{
return View(new PersonModel { ... });
}
In Global.asax.cs I have:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Word", "person/{name}",
new { controller = "Home", action = "GetPerson" });
routes.MapRoute(
"Default", "{controller}/{action}",
new { controller = "Home", action = "Index" });
}
In SomePage.cshtml I have, effectively, this:
#{ var name = "Winston S. Churchill"; }
#name
Problem
If I click the link for Winston S. Churchill, I am routed to the URL http://localhost/person/Winston%20S.%20Churchill, which yields the standard 404 page:
HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
This only happens if the name variable contains a . (period). All my code works perfectly fine when the name is, for example, Winston Churchill.
How can I make ASP.NET MVC 3 percent-encode the . (period) in the URL?
Or, how can I make the routing work without . (period) being percent-encoded?
Unacceptable Workaround (if presented without justification)
If I change the route to the following, everything works.
routes.MapRoute(
"Word", "person",
new { controller = "Home", action = "GetPerson" });
However, the URL becomes http://localhost/person?name=Winston%20S.%20Churchill, which isn't what I want. I want the name in the path part of the URL, not the query.
Routes which contain a period and unknown extension are interpreted by IIS as static files and not sent through the .NET pipeline. For example, the URL you cite is interpreted as a static file with a %20Churchill extension.
You can force ASP.NET to handle all requests by adding this to web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
You'll also need this, to handle name values that end with a period (as opposed to just containing one):
<system.web>
<httpRuntime relaxedUrlToFileSystemMapping="true" />
</system.web>
All /person/{name} URLs will then be picked up by your ASP.NET code.
If you would rather not use this setting, the easiest workaround would be to use a custom encoding:
name.Replace(".","--")
This error is very common, and I tried all of the solutions and non of them worked. I have disabled WebDAV publishing in control panel and added this to my web config file:
<handlers>
<remove name="WebDAV"/>
</handlers>
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule"/>
</modules>
The error still persists. This is the controller:
static readonly IProductRepository repository = new ProductRepository();
public Product Put(Product p)
{
return repository.Add(p);
}
Method implementation:
public Product Add(Product item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
item.Id = _nextId++;
products.Add(item);
return item;
}
And this is where the exception is thrown:
client.BaseAddress = new Uri("http://localhost:5106/");
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsJsonAsync("api/products", product);//405 exception
Any suggestions?
You are POSTing from the client:
await client.PostAsJsonAsync("api/products", product);
not PUTing.
Your Web API method accepts only PUT requests.
So:
await client.PutAsJsonAsync("api/products", product);
I had the same exception. My problem was that I had used:
using System.Web.Mvc; // Wrong namespace for HttpGet attribute !!!!!!!!!
[HttpGet]
public string Blah()
{
return "blah";
}
SHOULD BE
using System.Web.Http; // Correct namespace for HttpGet attribute !!!!!!!!!
[HttpGet]
public string Blah()
{
return "blah";
}
My problem turned out to be Attribute Routing in WebAPI. I created a custom route, and it treated it like a GET instead of WebAPI discovering it was a POST
[Route("")]
[HttpPost] //I added this attribute explicitly, and it worked
public void Post(ProductModel data)
{
...
}
I knew it had to be something silly (that consumes your entire day)
I tried many thing to get DELETE method work (I was getting 405 method not allowed web api) , and finally I added [Route("api/scan/{id}")] to my controller and was work fine.
hope this post help some one.
// DELETE api/Scan/5
[Route("api/scan/{id}")]
[ResponseType(typeof(Scan))]
public IHttpActionResult DeleteScan(int id)
{
Scan scan = db.Scans.Find(id);
if (scan == null)
{
return NotFound();
}
db.Scans.Remove(scan);
db.SaveChanges();
return Ok(scan);
}
This error can also occur when you try to connect to http while the server is on https.
It was a bit confusing because my get-requests were OK, the problem was only present with post-requests.
Chrome often times tries to do an OPTIONS call before doing a post. It does this to make sure the CORS headers are in order. It can be problematic if you are not handling the OPTIONS call in your API controller.
public void Options() { }
I'm late to this party but as nothing above was either viable or working in most cases, here is how this was finally resolved for me.
On the server the site/service was hosted on, a feature was required!
HTTP ACTIVATION!!!
Server Manager > Manage > Add Roles and Features > next next next till you get to Features > Under .NET (each version) tick HTTP Activation.
Also note there is one hidden under >net > WCF Services.
This then worked instantly!
That was melting my brain
I was getting the 405 on my GET call, and the problem turned out that I named the parameter in the GET server-side method Get(int formId), and I needed to change the route, or rename it Get(int id).
You can also get the 405 error if say your method is expecting a parameter and you are not passing it.
This does NOT work ( 405 error)
HTML View/Javascript
$.ajax({
url: '/api/News',
//.....
Web Api:
public HttpResponseMessage GetNews(int id)
Thus if the method signature is like the above then you must do:
HTML View/Javascript
$.ajax({
url: '/api/News/5',
//.....
If you have a route like
[Route("nuclearreactors/{reactorId}")]
You need to use the exact same parameter name in the method e.g.
public ReactorModel GetReactor(reactorId)
{
...
}
If you do not pass the exact same parameter you may get the error "405 method not allowed" because the route will not match the request and WebApi will hit a different controller method with different allowed HTTP method.
This does not answer your specific question, but when I had the same problem I ended up here and I figured that more people might do the same.
The problem I had was that I had indeliberately declared my Get method as static. I missed this an entire forenoon, and it caused no warnings from attributes or similar.
Incorrect:
public class EchoController : ApiController
{
public static string Get()
{
return string.Empty;
}
}
Correct:
public class EchoController : ApiController
{
public string Get()
{
return string.Empty;
}
}
Here is one solution:
<handlers accessPolicy="Read, Script">
<remove name="WebDAV" />
</handlers>
learn.microsoft.com solution article
and remove WebDAV from modules
<remove name="WebDAVModule" />
[HttpPost] is unnecessary!
[Route("")]
public void Post(ProductModel data)
{
...
}
I could NOT solve this. I had CORS enabled and working as long as the POST returned void (ASP.NET 4.0 - WEBAPI 1). When I tried to return a HttpResponseMessage, I started getting the HTTP 405 response.
Based on Llad's response above, I took a look at my own references.
I had the attribute [System.Web.Mvc.HttpPost] listed above my POST method.
I changed this to use:
[System.Web.Http.HttpPostAttribute]
[HttpOptions]
public HttpResponseMessage Post(object json)
{
...
return new HttpResponseMessage { StatusCode = HttpStatusCode.OK };
}
This fixed my woes. I hope this helps someone else.
For the sake of completeness, I had the following in my web.config:
<httpProtocol>
<customHeaders>
<clear />
<add name="Access-Control-Expose-Headers " value="WWW-Authenticate"/>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS, PUT, PATCH, DELETE" />
<add name="Access-Control-Allow-Headers" value="accept, authorization, Content-Type" />
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>
Old question but none of the answers worked for me.
This article solved my problem by adding the following lines to web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="false">
<remove name="WebDAVModule" />
</modules>
</system.webServer>
In my case I had a physical folder in the project with the same name as the WebAPI route (ex. sandbox) and only the POST request was intercepted by the static files handler in IIS (obviously).
Getting a misleading 405 error instead of the more expected 404, was the reason it took me long to troubleshoot.
Not easy to fall-into this, but possible. Hope it helps someone.
Make sure your controller inherits from Controller class.
It might even be crazier that stuff would work locally even without that.
For my part my POST handler was of this form:
[HttpPost("{routeParam}")]
public async Task<ActionResult> PostActuality ([FromRoute] int routeParam, [FromBody] PostData data)
I figured out that I had to swap the arguments, that is to say the body data first then the route parameter, as this:
[HttpPost("{routeParam}")]
public async Task<ActionResult> PostActuality ([FromBody] PostData data, [FromRoute] int routeParam)
check in your project .csproj file and change
<IISUrl>http://localhost:PORT/</IISUrl>
to your website url like this
<IISUrl>http://example.com:applicationName/</IISUrl>
Another possible issue which causes the same behavior is the default parameters in the routing. In my case the controller was located and instantiated correctly, but the POST was blocked because of default Get action specified:
config.Routes.MapHttpRoute(
name: "GetAllRoute",
routeTemplate: "api/{controller}.{ext}"/*,
defaults: new { action = "Get" }*/ // this was causing the issue
);
I was having exactly the same problem. I looked for two hours what was wrong with no luck until I realize my POST method was private instead of public .
Funny now seeing that error message is kind of generic. Hope it helps!
We had a similar issue. We were trying to GET from:
[RoutePrefix("api/car")]
public class CarController: ApiController{
[HTTPGet]
[Route("")]
public virtual async Task<ActionResult> GetAll(){
}
}
So we would .GET("/api/car") and this would throw a 405 error.
The Fix:
The CarController.cs file was in the directory /api/car so when we were requesting this api endpoint, IIS would send back an error because it looked like we were trying to access a virtual directory that we were not allowed to.
Option 1: change / rename the directory the controller is in
Option 2: change the route prefix to something that doesn't match the virtual directory.
In my case, the 405 error only showed up in production server, and not on my dev machine.
I found that the problem was due to the fact that I simply "manually" transferred the contents of the locally published folder from my local machine to the online production server.
So, the FIX for me was to simply delete all the online files on the prod server, and then use the "Publish" option on Visual Studio to publish directly from my local machine to the prod server via FTP.
I don't know exactly why this changed something, because it seems to me the files were the same, but this thing fixed the problem and I hope it could help someone else too.
Another possible cause can be to do with Session State config in IIS causing a redirect which appends "?AspxAutoDetectCookieSupport=1" to the URL. In my case I was performing a POST but the redirect was being performed as a GET by the HttpClient.
The solution I found was to add the following to my web.config:
<system.web>
<sessionState cookieless="UseCookies" />
</system.web>
Function names make it complicated for c# sometimes. Change name of the function, it will works. Like ProductPut instead of PutProduct or Put.
public Product ProductPut(Product p)
{
return repository.Add(p);
}
With the default web api route
config.Routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new
{
id = RouteParameter.Optional
}
);
and a controller
public class TestController : ApiController
{
[HttpGet]
public HttpResponseMessage Get(string id)
{
return Request.CreateResponse(HttpStatusCode.OK, id);
}
}
A request to 'api/test/1'
returns 1
If for some reason you send a request to 'api/test/1%20'
the route 404's.
Now this example may seem silly since browsers trim trailing spaces, but
for a route like 'api/{controller}/{id}/{extrastuff}'
the space in '1 ' would convert to '1%20' and the request will 404 on the route not being found.
Your issue has nothing to do with WebAPI itself but how Asp.Net handles some specific urls.
And Asp.Net handles these urls in a very paranoid way, so you need to tell it to relax.
Add this line to your web.config under system.web:
<httpRuntime relaxedUrlToFileSystemMapping="true" />
You can read more about this topic:
Putting the Con (COM1, LPT1, NUL, etc.) Back in your URLs
Also on SO:
"The resource cannot be found." error when there is a "dot" at the end of the url
Problem with a URL that ends with %20 (it describes a different context so I don't think that this is a real duplicate)
Add this to handlers
<add name="ExtensionlessUrlHandler-Integrated-4.0-ForApi"
path="api/*"
verb="*"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>