ASP.NET Web API CORS not working with AngularJS - c#

I have an ASP.NET Web API running locally on some port and I have an angularjs app running on 8080. I want to access the api from the client.
I can successfully login and register my application because in my OAuthAuthorizationProvider explicitly sets the repsonse headers in the /Token endpoint.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
That's good. However, my other API methods do not seem to work. In my WebApiCongig.Register, I enable CORS and I add the EnableCors Attribute to my controllers to allow all origins, all headers, and all methods. I can set a break point in my get method on the controller and it gets hit just fine. Here is what I found watching the Network tab in chrome.
2 requests are are sent to the same api method. One method type OPTIONS and one with method type GET. The OPTIONS request header includes these two lines
Access-Control-Request-Headers:accept, authorization
Access-Control-Request-Method:GET
And the response includes these lines
Access-Control-Allow-Headers:authorization
Access-Control-Allow-Origin:*
However, the GET method request looks quite different. It returns ok with a status code of 200, but it does not inlcude and access control headers in the request or response. And like I said, it hits the API just fine. I can even do a POST and save to the database, but the client complains about the response!!
I've looked at every single SO question and tried every combination of enabling cors. I'm using Microsoft.AspNet.Cors version 5.2.2. I'm' using AngularJS version 1.3.8. I'm also using the $resource service instead of $http which doesn't seem to make a difference either.
If I can provide more information, please let me know.
BTW, I can access the Web API using Fiddler and/or Postman by simply including the Bearer token.

You don't seem to be handling the preflight Options requests.
Web API needs to respond to the Options request in order to confirm that it is indeed configured to support CORS.
To handle this, all you need to do is send an empty response back. You can do this inside your actions, or you can do it globally like this:
protected void Application_BeginRequest()
{
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
Response.Flush();
}
}
This extra check was added to ensure that old APIs that were designed to accept only GET and POST requests will not be exploited. Imagine sending a DELETE request to an API designed when this verb didn't exist. The outcome is unpredictable and the results might be dangerous.
Also I suggest enabling Cors by web.config instead of config.EnableCors(cors);
This can be done by adding some custom headers inside the <system.webServer> node.
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
Please note that the Methods are all individually specified, instead of using *. This is because there is a bug occurring when using *.

This ended up being a simple fix. Simple, but it still doesn't take away from the bruises on my forehead. It seems like the more simple, the more frustrating.
I created my own custom cors policy provider attribute.
public class CorsPolicyProvider : Attribute, ICorsPolicyProvider
{
private CorsPolicy _policy;
public CorsPolicyProvider()
{
// Create a CORS policy.
_policy = new CorsPolicy
{
AllowAnyMethod = true,
AllowAnyHeader = true,
AllowAnyOrigin = true
};
// Magic line right here
_policy.Origins.Add("*");
}
public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(_policy);
}
}
I played around with this for hours. Everything should work right?? I mean the EnableCors attribute should work too?? But it didn't. So I finally added the line above to explicitly add the origin to the policy. BAM!! It worked like magic. To use this just add the attribute to your api class or method you want to allow.
[Authorize]
[RoutePrefix("api/LicenseFiles")]
[CorsPolicyProvider]
//[EnableCors(origins: "*", headers: "*", methods: "*")] does not work!!!!! at least I couldn't get it to work
public class MyController : ApiController
{

in my case, after I changed the Identity option of my AppPool under IIS from ApplicationPoolIdentity to NetworkService, CORS stopped working in my app.

Related

How to allow CORS for ASP.NET WebForms endpoint?

I am trying to add some [WebMethod] annotated endpoint functions to a Webforms style web app (.aspx and .asmx).
I'd like to annotate those endpoints with [EnableCors] and thereby get all the good ajax-preflight functionality.
VS2013 accepts the annotation, but still the endpoints don't play nice with CORS. (They work fine when used same-origin but not cross-origin).
I can't even get them to function cross-origin with the down and dirty
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", "*");
approach -- my browsers reject the responses, and the cross-origin response headers don't appear.
How can I get CORS functionality in these [WebMethod] endpoints?
I recommend double-checking you have performed all steps on this page: CORS on ASP.NET
In addition to:
Response.AppendHeader("Access-Control-Allow-Origin", "*");
Also try:
Response.AppendHeader("Access-Control-Allow-Methods","*");
Try adding directly in web config:
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Methods" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
</customHeaders>
</httpProtocol>
</system.webServer>
Failing that, you need to ensure you have control over both domains.
FYI, enable CORS in classic webform. In Global.asax
void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configuration.EnableCors();
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
If you need the preflight request, e.g. so you can send authenticated requests, you are not able to set Access-Control-Allow-Origin: *. It must be a specific Origin domain.
Also you must set the Access-Control-Allow-Methods and Access-Control-Allow-Headers response headers, if you are using anything besides the defaults.
(Note these constraints are just how CORS itself works - this is how it is defined.)
So, it's not enough to just throw on the [EnableCors] attribute, you have to set values to the parameters:
[EnableCors(origins: "https://www.olliejones.com", headers: "X-Custom-Header", methods: "PUT", SupportsCredentials = true)]
Or if you want to do things manually and explicitly:
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", "https://www.olliejones.com");
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Headers", "X-Custom-Header");
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Methods", "PUT");
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Credentials", "true");
One last thing - you do have to call .EnableCors() on initiation. In e.g. MVC or WebAPI, you would call this on HttpConfiguration, when registering the config and such - however I have no idea how it works with WebForms.
If you use the AppendHeader method to send cache-specific headers and at the same time use the cache object model (Cache) to set cache policy, HTTP response headers that pertain to caching might be deleted when the cache object model is used. This behavior enables ASP.NET to maintain the most restrictive settings. For example, consider a page that includes user controls. If those controls have conflicting cache policies, the most restrictive cache policy will be used. If one user control sets the header "Cache-Control: Public" and another user control sets the more restrictive header "Cache-Control: Private" via calls to SetCacheability, then the "Cache-Control: Private" header will be sent with the response.
You can create a httpProtocol in web config for customHeaders.
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Methods" values="*" />
</customHeaders>
<httpProtocol>
I think your code looks good, but IIS does not send the header entity alone with response expectedly. Please check whether IIS is configured properly.
Configuring IIS6
Configuring IIS7
If CORS doesn't work for your particularity problem, maybe jsonp is another possible way.
For the web form, you can use
Response.AddHeader("Access-Control-Allow-Origin", "*");
instead of
Response.AppendHeader("Access-Control-Allow-Origin", "*");
The first one works for old version of ASP.Net Web Form.
You can do like this in MVC
[EnableCors(origins: "*", headers: "*", methods: "*")]
public ActionResult test()
{
Response.AppendHeader("Access-Control-Allow-Origin", "*");
return View();
}

Allow frame from different domain with MVC5

I am trying to load google maps into an iframe using MVC5 but I am getting blocked with the error
Refused to display 'https://www.google.com/maps?cid=XXXXX' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'.
So after much searching, I have tried the following:
Adding AntiForgeryConfig.SuppressXFrameOptionsHeader = true; to the Application_Start in global.ascx
Creating an attribute (have tried this with and without the setting in global.ascx):
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext != null)
{
filterContext.HttpContext.Response.Headers["X-Frame-Options"] = "ALLOW-FROM https://www.google.com";
base.OnActionExecuted(filterContext);
}
}
trying the attribute OnResultExecuted(ResultExecutedContext filterContext) instead of OnActionExecuted
remove it in the web.config:
<httpProtocol>
<customHeaders>
<remove name="X-Frame-Options" />
</customHeaders>
</httpProtocol>
Is there something I'm missing? how do I get rid of this http header (or at least change it to allow maps)?
Update
I have just checked the headers being sent and they are correct in that they either say
X-Frame-Options: ALLOW-FROM https://www.google.com
Or aren't there at all if I remove the attribute but keep the global.ascx update
Yet when I run the page and see these headers, it is still giving me the SAMEORIGIN error.
As it turns out I have been completely stupid and misunderstood how x-frame-options work. It is to stop your site page being shown on another site through an iframe.
So the x-frame-options http header that I was getting for SAMEORIGIN was actually coming from google. I thought that as the url was returned from their places api I could just use it, but apparently you can only link to it.
Creating a new map api key and enabling the maps embed api, I was able to use the place_id instead and call the following url into the iframe:
https://www.google.com/maps/embed/v1/place?key=YOUR_API_KEY&q=place_id:PLACE_ID
And this would show without me getting the header (or doing anything extra to my headers).
I'll leave this here just in case anyone is as daft as I am

Cors issues on WebAPI

Enabling Cors on WebAPI
I have this set in WebApiConfig.cs
config.EnableCors();
and this is how my attribute is setup for my controller method:
[EnableCors("http://dev.example.com,http://personal.example.com,http://www.example.com", // Origin
"Accept, Origin, Content-Type, Options", // Request headers
"POST", // HTTP methods
PreflightMaxAge = 600 // Preflight cache duration
)]
But I still get the error: "The 'Access-Control-Allow-Origin' header contains multiple values."
What else do I need to do to prevent this? We must allow from all three domains. but the first 2 are sub-domains of the last one.
do you have any options set into your web.config file for cors ? i.e something like <add name="Access-Control-Allow-Origin" value="*"/>
if yes make sure to remove that, and control the cors through the code only.
Edit:
well, that means that you always add the header to your response, no matter which controller the request hits, and in case the request hits the controller with the EnableCors attribute it will add another header. If you removed the one in the Application_BeginRequest() it should work, however that means that you need to decorate all other controllers with EnableCors attribute, which maybe acceptable in your case, otherwise, you need to add a DelegateHandler where you can check the request and set the cors depending on the requested controller. have a look at this http://georgedurzi.com/implementing-cross-browser-cors-support-for-asp-net-web-api/ it may help start with DelegateHandlers. Hope that helps.

Enable CORS in Web API 2

I have client and a server running on different ports. The server is running Web API 2 (v5.0.0-rc1).
I tried installing the Microsoft ASP.NET Web API Cross-Origin Support package and enabled it in WebApiConfig.cs. It gives me the EnableCors() function, so the package was installed correctly.
Here you can see my Register() function in WebApiConfig.cs:
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
}
GET requests work fine. But when sending POST, I get the following:
OPTIONS http://localhost:19357/api/v1/rooms? 404 (Not Found) angular.js:10159
OPTIONS http://localhost:19357/api/v1/rooms? Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin. angular.js:10159
XMLHttpRequest cannot load http://localhost:19357/api/v1/rooms. Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin.
According to Fiddler it only sends the OPTIONS request. It doesn't issue the POST afterwards.
So I'm guessing the config.EnableCors(cors); in the WebApiConfig.cs isn't doing anything, which leads to the server denying the client/browser to send a POST request.
Do you have any idea how to solve this problem?
EDIT 05.09.13
This has been fixed in 5.0.0-rtm-130905
CORS works absolutely fine in Microsoft.AspNet.WebApi.Cors version 5.2.2. The following steps configured CORS like a charm for me:
Install-Package Microsoft.AspNet.WebApi.Cors -Version "5.2.2" // run from Package manager console
In Global.asax, add the following line: BEFORE ANY MVC ROUTE REGISTRATIONS
GlobalConfiguration.Configure(WebApiConfig.Register);
In the WebApiConfig Register method, have the following code:
public static void Register(HttpConfiguration config)
{
config.EnableCors();
config.MapHttpAttributeRoutes();
}
In the web.config, the following handler must be the first one in the pipeline:
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
In the controller derived from ApiController, add the EnableCorsAttribute:
[EnableCors(origins: "*", headers: "*", methods: "*")] // tune to your needs
[RoutePrefix("")]
public class MyController : ApiController
That should set you up nicely!
I didn't need to install any package. Just a simple change in your WebAPI project's web.config is working great:
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
Credit goes to: Using CORS in ASP.NET WebAPI Without Being a Rocket Scientist
For reference using the [EnableCors()] approach will not work if you intercept the Message Pipeline using a DelegatingHandler. In my case was checking for an Authorization header in the request and handling it accordingly before the routing was even invoked, which meant my request was getting processed earlier in the pipeline so the [EnableCors()] had no effect.
In the end found an example CrossDomainHandler class (credit to shaunxu for the Gist) which handles the CORS for me in the pipeline and to use it is as simple as adding another message handler to the pipeline.
public class CrossDomainHandler : DelegatingHandler
{
const string Origin = "Origin";
const string AccessControlRequestMethod = "Access-Control-Request-Method";
const string AccessControlRequestHeaders = "Access-Control-Request-Headers";
const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
bool isCorsRequest = request.Headers.Contains(Origin);
bool isPreflightRequest = request.Method == HttpMethod.Options;
if (isCorsRequest)
{
if (isPreflightRequest)
{
return Task.Factory.StartNew(() =>
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Headers.Add(AccessControlAllowOrigin, request.Headers.GetValues(Origin).First());
string accessControlRequestMethod = request.Headers.GetValues(AccessControlRequestMethod).FirstOrDefault();
if (accessControlRequestMethod != null)
{
response.Headers.Add(AccessControlAllowMethods, accessControlRequestMethod);
}
string requestedHeaders = string.Join(", ", request.Headers.GetValues(AccessControlRequestHeaders));
if (!string.IsNullOrEmpty(requestedHeaders))
{
response.Headers.Add(AccessControlAllowHeaders, requestedHeaders);
}
return response;
}, cancellationToken);
}
else
{
return base.SendAsync(request, cancellationToken).ContinueWith(t =>
{
HttpResponseMessage resp = t.Result;
resp.Headers.Add(AccessControlAllowOrigin, request.Headers.GetValues(Origin).First());
return resp;
});
}
}
else
{
return base.SendAsync(request, cancellationToken);
}
}
}
To use it add it to the list of registered message handlers
config.MessageHandlers.Add(new CrossDomainHandler());
Any preflight requests by the Browser are handled and passed on, meaning I didn't need to implement an [HttpOptions] IHttpActionResult method on the Controller.
I'm most definitely hitting this issue with attribute routing. The issue was fixed as of 5.0.0-rtm-130905. But still, you can try out the nightly builds which will most certainly have the fix.
To add nightlies to your NuGet package source, go to Tools -> Library Package Manager -> Package Manager Settings and add the following URL under Package Sources: http://myget.org/F/aspnetwebstacknightly
Make sure that you are accessing the WebAPI through HTTPS.
I also enabled cors in the WebApi.config.
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
But my CORS request did not work until I used HTTPS urls.
Late reply for future reference. What was working for me was enabling it by nuget and then adding custom headers into web.config.
var cors = new EnableCorsAttribute("*","*","*");
config.EnableCors(cors);
var constraints = new {httpMethod = new HttpMethodConstraint(HttpMethod.Options)};
config.Routes.IgnoreRoute("OPTIONS", "*pathInfo",constraints);
This solved my issue:
in Web.config>>Inside system.webServer tags:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET,POST,PUT, DELETE, OPTIONS" />
<add name="Access-Control-Allow-Headers" value="*" />
<add name="Access-Control-Allow-Credentials" value="true" />
</customHeaders>
</httpProtocol>
And uncomment or delete this line inside system.webServer:
<remove name="OPTIONSVerbHandler"/>
Also, you need to declare enable cors either in webapiconfig.cs or in web.config. If you declare in both you may get an error something like :
'....origin 'www.example.com' blocked by cors policy due to multiple No 'Access-Control-Allow-Origin'('*','*').'
This did the trick for me.
To enable CORS,
1.Go to App_Start folder.
2.add the namespace 'using System.Web.Http.Cors';
3.Open the WebApiConfig.cs file and type the following in a static method.
config.EnableCors(new EnableCorsAttribute("https://localhost:44328",headers:"*", methods:"*"));
As far as I understood, the server got to have a header that specifies that Access from Origin is Allowed i.e. a request from the same server could be responded to.I used the following code :
// create a response object of your choice
var response = Request.CreateResponse(HttpStatusCode.OK);
//add the header
//replace the star with a specific name if you want to restrict access
response.Headers.Add("Access-Control-Allow-Origin", "*");
//now you could send the response and it should work...
return response;
Below code worked for me,
App_Start -> WebApiConfig
EnableCorsAttribute cors = new EnableCorsAttribute("\*", "\*", "GET,HEAD,POST");
config.EnableCors(cors);

405 method not allowed Web API

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);
}

Categories

Resources