I am trying to setup a .net core 2.2 web api to use a post verb. Anything other than a get verb returns a 405 no matter if it is run on my local machine (w10 iis eXPRESS 10.0) or the windows server (2016 R2 IIS 8.0). I've read the other posts about disabling WebDav in your config file, adding a route, and completely removing the WebDav feature. I have done all of those to no avail. I'm just starting to develop in core and find this baffling, on the same server is a non-core web api that runs on .net framework 4.5 that processes GET,PUT,POST,DELETE without error. And yes, I have restarted the server after making changes to any of the configurations. The following are the web.config changes that I made, the last one coming directly from MS. Basic project that reproduces the same error on my machine and server is here https://github.com/FranciscanMedia/error405_core/tree/master it is just a standard web api project you get when you fire up VS2019.
<system.webServer>
<handlers accessPolicy="Read, Script">
<remove name="WebDAV" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit"
path="*."
verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
modules="IsapiModule"
scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll"
preCondition="classicMode,runtimeVersionv4.0,bitness64"
responseBufferLimit="0" />
</handlers>
</system.webServer>
<system.webServer>
<modules>
<remove name="WebDAVModule" />
</modules>
<handlers>
<remove name="WebDAV" />
</handlers>
</system.webServer>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="false">
<remove name="WebDAVModule"/>
</modules>
</system.webServer>
<system.webServer>
<handlers accessPolicy="Read, Script">
<remove name="WebDAV" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit"
path="*."
verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
modules="IsapiModule"
scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll"
preCondition="classicMode,runtimeVersionv4.0,bitness64"
responseBufferLimit="0" />
</handlers>
</system.webServer>
Short answer
It could be as simple as that. The reason is routing.
Just send your POST request to right URL like https://localhost:44327/api/values/123.
Detailed explanation
It's not the issue. It works as expected.
You make a GET request to https://localhost:44327/api/values/. It returns 200 OK.
But when you make a POST request to the same URL https://localhost:44327/api/values/. It says 405 Method not allowed.
However, you get 405. It is happening because you are hitting the GET endpoint with POST method.
Microsoft Docs says:
... the HTTP client sent a valid JSON request to the URL for a Web API application on a web server, but the server returned an HTTP 405 error message which indicates that the PUT method was not allowed for the URL. In contrast, if the request URI did not match a route for the Web API application, the server would return an HTTP 404 Not Found error.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/testing-and-debugging/troubleshooting-http-405-errors-after-publishing-web-api-applications
If you simply remove the GET endpoint. The POST request will start returning 404 Not found. Which means that you are not hitting any registered route.
To send POST request you need to use different URL according to the routing rules.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// POST api/values
[HttpPost("{val}")]
public StatusCodeResult Post()
{
return Ok();
}
}
This attribute-based configuration means that route of your POST endpoint is /api/Values/{val}. Where {val} is any value. It's not processed in the endpoint.
If you want to process it, you should pass it to the method:
[HttpPost("{val}")]
public StatusCodeResult Post(string val)
{
return Ok();
}
I think that in your controller you have to import another library.
Try
using System.Web.Http;
Instead of
using Microsoft.AspNetCore.Mvc
Looking at what you have defined:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
Then for the action:
[HttpPost("{val}")]
public StatusCodeResult Post()
{
return Ok();
}
Your routing matches the following url:
https://localhost:44327/api/values/StatusCodeResult
It is going to take your main route defined on your controller [Route("api/[controller]")]
Then you are defining the "template" to use "{val}"
This is telling it to use the ActionResult specific name and to expect var val to be passed/appened.
Checking out the official documentation here: https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-2.2
under section "Token replacement in route templates ([controller], [action], [area])"
They specifiy:
For convenience, attribute routes support token replacement by enclosing a token in square-braces ([, ]). The tokens [action], [area], and [controller] are replaced with the values of the action name, area name, and controller name from the action where the route is defined. In the following example, the actions match URL paths as described in the comments:
[Route("[controller]/[action]")]
public class ProductsController : Controller
{
[HttpGet] // Matches '/Products/List'
public IActionResult List() {
// ...
}
[HttpGet("{id}")] // Matches '/Products/Edit/{id}'
public IActionResult Edit(int id) {
// ...
}
}
If you want it to just route based on just verbs (follow a pattern where each api endpoint just handles operations for that specific object) then you would change your post method to just
[HttpPost]
public ActionResult Post(string val)
{
return Ok();
}
I totally agree with #Vladimir's answer. I dont have enough points to add comments to the answer by #vlaimir so i am adding my thoughts and suggestions.
The code you have on your github,
// POST api/values
[HttpPost("{val}")]
public StatusCodeResult Post()
{
return Ok();
}
This is a post and it would expect a value {val} per the route action configuration. Since you may try to hit the post without any value, thats not permitted. Ensure you supply some value and then do the POST. If you are using POSTMAN, you may have to supply the BODY of your request with some value. Swagger is a great util tool to embed into the web api's and that comes with excellent intuitive UI for our routes/resources. That might be even ideal to help determine and ensure you supply the right value.
Otherwise, you dont need to modify or worry about your IIS or IIS Express settings. or webdav.
Related
Hi Guys I need help.. I always get 405 Method Not Allowed
I'm using Asp.Net Core Web Application 3.1, I dont have problem with HttpGet but when i use HttpPost it always return 405 Status Code
Here is the My Controller
[Route("api/[controller]")]
[ApiController]
public class ExamController : ControllerBase
{
[HttpPost("PostValue")]
public ActionResult<HttpResponseMessage> PostInfo([FromBody] PersonalInfo info)
{
string json = JsonConvert.SerializeObject(info);
HttpClient client = new HttpClient();
var response = client.PostAsync("https://sampleapi/receive", new StringContent(json, Encoding.UTF8, "application/json"));
if (response.IsFaulted)
return BadRequest(response);
return Ok(response);
}
}
This is my Startup Class
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddCors(c =>
{
c.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin());
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStatusCodePages();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseCors(options => options.AllowAnyOrigin());
}
Here is the sample image of URL and the Result
Looking at the provided image, you use chrome to issue the url request, which is a HTTP GET command. So, your app got an HTTP GET command but your method wants to accept an HTTP POST method. That's why it says 'method not allowed'.
If you want to try http commands, you need a web test tool such as PostMan.
Configure the CORS middleware properly. Add .AllowAnyMethod() after options.AllowAnyOrigin() as a chain. You may end up to this for testing purposes: app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
As another option, make sure that your web server (likely IIS) allows POST HTTP method besides GET.
In addition to the postman test method, another way to test the post request is to use ajax to send post request in jquery, here is a sample:
<script>
$(function () {
$("#send").click(function () {
var personalInfo = { Id: $('#Id').val(), Name: $('#Name').val() };
$.ajax({
url: 'http://localhost:50855/api/Exam/PostValue',
type: 'POST',
contentType: "application/json; charset=utf-8",
dataType: 'json',
data: JSON.stringify(personalInfo),
//success: function (data, textStatus, xhr) {
// console.log(data);
//},
//error: function (xhr, textStatus, errorThrown) {
// console.log('Error in Operation');
//}
});
});
})
</script>
<form id="form1">
Id : <input type="text" name="Id" id="Id" />
Name: <input type="text" name="Name" id="Name" />
<input type="button" id="send" value="Send Post Data" />
</form>
Here is the test result:
I'm just going to add to this thread with my issue and resolution to same in case someone else stumbles upon this.
I was receiving a 405 error code, despite the action being decorated with the HttpPost and ValidateAntiForgeryToken attributes, and ensuring the posted form data included the anti-forgery token. Everything worked fine locally, but as soon as I put everything up on the server that's when I started receiving the 405 error. Turns out this error had nothing to do with what I had done in my app. It was actually an issue in a stored procedure in my MySQL database. Locally, case sensitivity isn't an issue, but on the server I had upper-cased the name of a table that was in lower-case, which caused an error to bubble up and give me this very obscure 405 error.
I guess lesson learned here is to check EVERYTHING :)
Edit: For clarity, my issue relates to MySQL and Linux Hosting. Not sure if same would apply if using Windows hosting solutions
My solution was literally as stupid as adding a "/" to the end of my request URL. Lost hours over it. GET worked fine without it.
I am working on .Net 5 Api project, and came across this same issue.
Adding lines below to the auto-generated web.config file when release is done:
<modules>
<remove name="WebDAVModule" />
</modules>
to the <system.webServer> part of web.config and
<handlers>
<remove name="WebDAV" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,PUT,DELETE,DEBUG" type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" responseBufferLimit="0" />
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
fixed it for me. Some of the lines are probably a bit of an overkill, but I have philosophy "if it may not work in future, better to have it overengineered".
However, I was struggling a lot with Azure DevOps CI-CD pipelines.
I managed to do it, and wrote about how I did it here:
Azure DevOps Release Pipeline Web.Config Edit
Okay so I've been reading about removing the WebDav by entering in the web.config but that didn't work for me for core 3.1. What you need to do is remove it from the IIS by:
Expand the server in IIS 8
Select the site
Click on handling mappings (For the site not server)
Search for WebDav and remove it.
Restart IIS
Web.config should be left alone. when i added the remove name=WebDav, my API stopped working.
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
note: you may need to also remove the one from the server if that doesn't work as I removed that first when I was troubleshooting.
Add [Route("MethodName")] in the header of the method in the controller. Just like:
[Route("api/[controller]")]
[ApiController]
public class AccountController : Controller
{
[Route("getusers")]
public async Task<User> GetUsers()
{
...
}
}
I want to enable CORS on one specific action in an Asp.net Web Api. Here's how I'm trying to do it:
[Route("api/mycontroller/myaction")]
[HttpPost]
[EnableCors("https://example.com", "*", "post")]
public async Task<IHttpActionResult> MyAction()
{
...
}
But when I send an OPTIONS request to the route, I get back an error: "The requested resource does not support http method 'OPTIONS'." I also tried removing the [HttpPost] annotation to no avail.
What am I missing?
For me, I added the following headers to the request by adding the following code to the Application_BeginRequest function of the Global.asax.cs file:
protected void Application_BeginRequest()
{
if (Request.Headers.AllKeys.Contains("Origin", StringComparer.CurrentCultureIgnoreCase)
&& Request.HttpMethod == "OPTIONS")
{
Response.AddHeader("Access-Control-Allow-Headers", "content-type", "accept", "pragma", "cache-control", "authorization");
Response.End();
}
}
I have little idea why this works.
Out of curiosity, I tried adding all headers by using an asterisk but then Web API complained that the Authorization header was missing.
You've probably missed the higher level call to HttpConfiguration.EnableCors, as described here: https://enable-cors.org/server_aspnet.html.
Add this code to your configuration:
public static void Register(HttpConfiguration config)
{
// New code
config.EnableCors();
}
To ensure the OPTIONS request gets handled by your application code and not some other part of the system before it reaches your app code, you may try adding the following to your web.config:
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
You might also need to include:
<add name="OPTIONSVerbHandler" path="*" verb="OPTIONS"
modules="IsapiModule" requireAccess="None"
scriptProcessor="C:\Windows\System32\inetsrv\asp.dll"
resourceType="Unspecified" />
See the answer at IIS hijacks CORS Preflight OPTIONS request.
Or maybe even just this:
<add name="OPTIONSVerbHandler" path="*" verb="OPTIONS"
modules="ProtocolSupportModule" requireAccess="None" />
If none of that on its own works, then in your global.asax or other code you might try:
if (filterContext.HttpContext.Request.HttpMethod == "OPTIONS")
{
filterContext.HttpContext.Response.Flush();
}
…or some other variation on that, for example:
if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OridinalIgnoreCase)
&& Request.HttpMethod == "OPTIONS") {
Response.Flush();
}
Regardless of what specific code you use to do it, the point is to:
make sure OPTIONS requests are actually getting caught/handled by your application code—not caught/handled by some other part of the system before ever reaching your app code
make sure you have explicit handling for OPTIONS requests in your application code
make the OPTIONS handling in your application code just do Response.Flush()
Or another approach I’m not sure is relevant to your situation as coded but I’ll mention just in case:
public HttpResponseMessage Options()
{
var response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
};
return response;
}
I want to build a SPA with AngularJS and ASP.NET Web API.
In regards to the frontend webpage I would like to limit the implication of asp.net as much as possible and move all the logic into Angular, the only thing the Web API will supply is a REST service.
I have created an index.html page with some angular that loads a basic list from the server.
But my index.html is accessed using ex. http://localhost:1234/app/index.html , what I would like now is to see my index.html from http://localhost:1234/ and also get a custom error page if I access some random link from this host.
Do I require ASP.NET to do this ? I would like to limit the use of ASP.NET as much as possible, only the basic required stuff.
And I am complete new to this.
Web.config :
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<clear />
<add
name="StaticFile"
path="*" verb="*"
modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule"
resourceType="Either"
requireAccess="Read" />
</handlers>
<staticContent>
<mimeMap fileExtension=".*" mimeType="application/octet-stream" />
</staticContent>
<defaultDocument enabled="true">
<files>
<clear/>
<add value="index.html"/>
</files>
</defaultDocument>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Also added routing:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
Page error:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /
Well angular only does routing on client side, for your url needs you will still need to do some config on server side.
this should do the trick:
add this to web.config:
<system.webServer>
<defaultDocument>
<files>
<clear/>
<add value="(location of index.html)"/>
</files>
</defaultDocument>
As for errors (404 is among them), answer can be found here. That is only for when someone tries to get an url that does not exist, before angular has been loaded.
But once angular is loaded up, you do everything withing config, where you configure $routeProvider:
$routeProvider.when('/someUrl', {templateUrl: 'url/templatefile.html', controller: 'templateController'})
.when('/my404route', {templateUrl: 'url/my404file.html', controller: 'my404Controller'})
//when returns $routeprovider so you can chain "whens"
//finnaly add "otherwise"
.otherwise({redirectTo: '/my404route'});
//or just route to root with '/' if you won't handle unknown routes
By default visual studio deploy the site in a subfolder. To change it right click on your web project file / properties. Then in the Web tab specify the project url to be http://localhost:1234.
I have a Web API project and I am unable to enable "PUT/Patch" requests against it.
The response I get from fiddler is:
HTTP/1.1 405 Method Not Allowed
Cache-Control: no-cache
Pragma: no-cache
Allow: GET,POST,DELETE
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcUHJvamVjdHNcZG90TmV0XFdlYkFQSVxBZFNlcnZpY2VcQWRTZXJ2aWNlXGFwaVxpbXByZXNzaW9uXDE1?=
X-Powered-By: ASP.NET
Date: Tue, 06 May 2014 14:10:35 GMT
Content-Length: 72
{"message":"The requested resource does not support http method 'PUT'."}
Based on the above response, "PUT" verbs are not accepted. However, I'm unable to figure out where the related handler is configured.
The "Put" method of class is declared as follows:
[HttpPatch]
[HttpPut]
public HttpResponseMessage Put(Int32 aID, [FromBody] ImpressionModel impressionModel)
{
bla, bla, bla, bla
}
I have read and implemented the changes explained in the following threads:
- Asp.NET Web API - 405 - HTTP verb used to access this page is not allowed - how to set handler mappings
- http://www.asp.net/web-api/overview/testing-and-debugging/troubleshooting-http-405-errors-after-publishing-web-api-applications
Nothing has worked as I'm still getting a 405 response when trying to issue a "PUT" command against my Web API project.
I even commented out all of the "Handlers" in the ApplicationsHost.config file.
Working with VS2012 Premium and IIS Express (I'm assuming it's version 8). I also tried the VS Dev Server but that gave me the same result also.
I'm out of ideas. Any help would be appreciated.
Thanks Lee
Are you using attribute routing?
This mystic error was a route attributes issue. This is enabled in your WebAPIConfig as:
config.MapHttpAttributeRoutes();
It turns out Web Api Controllers "cannot host a mixture of verb-based action methods and traditional action name routing. "; https://aspnetwebstack.codeplex.com/workitem/184
in a nutshell: I needed to mark all of my actions in my API Controller with the [Route] attribute, otherwise the action is "hidden" (405'd) when trying to locate it through traditional routing.
API Controller:
[RoutePrefix("api/quotes")]
public class QuotesController : ApiController
{
...
// POST api/Quote
[ResponseType(typeof(Quote))]
[Route]
public IHttpActionResult PostQuote(Quote quote)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Quotes.Add(quote);
db.SaveChanges();
return CreatedAtRoute("", new { id = quote.QuoteId }, quote);
}
note: my Route is unnamed so the CreatedAtRoute() name is just an empty string.
WebApiConfig.cs:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
hope this helps
I had the exact same problem as you, and I tried all the things you tried but sometimes the solution is so trivial and under your nose, that you just don't expect it and keep looking for more complicated reasons. Make sure that in the url you're calling to test your web methods, the param names match the names in your controller method declaration. My 405 problem was solved by simply doing this (I was using query params):
My clientsController:
...
[HttpPut]
public string PutClient(string username = "", string phone= ""){...}
On my WebApiConfig:
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}"
);
And the path used to test the method must be like so: (use Postman or similar, to apply the correct web method)
http://localhost:49216/api/clients?username=Kurt&phone=443332211
Otherwise, you'll get a 405 for that http method in that controller. I didn't need to change the web.config at all (no need to remove webdav, etc..). Check this for source in the documentation:
For example, consider the following action:
public void Get(int id)
The id parameter binds to the URI. Therefore,
this action can only match a URI that contains a value for "id",
either in the route dictionary or in the query string.
Optional parameters are an exception, because they are optional. For
an optional parameter, it's OK if the binding can't get the value from
the URI.
This happened to me when I changed the first parameter name of the PUT method
public void Put(int code, [FromBody]Lead lead)
It should be:
public void Put(int id, [FromBody]Lead lead)
And this is how it gets called:
$.ajax({
type: "PUT",
data: json,
url: "../api/leadsapi/123",
contentType: "application/json; charset=utf-8"
});
For me it was as many of the other posters had claimed. Chances are you have everything setup correctly in your webapiconfig, however you're just missing something silly.
In my case I had a route defined as:
[HttpPut]
[Route("api/MilestonePut/{Milestone_ID}")]
public void Put(int id, [FromBody]Milestone milestone)
{
db.Entry(milestone).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
}
See the problem? The parameter is defined as Milestone_ID in the route, but as id in the function itself. You would think .NET would be smart enough to realize this, but it definitely isn't and will not work.
Once I changed it to match the parameter like so:
[HttpPut]
[Route("api/MilestonePut/{id}")]
public void Put(int id, [FromBody]Milestone milestone)
{
db.Entry(milestone).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
}
everything worked like a charm.
This is also the error message returned if you forget to make the Put() method on your API controller public. Which is obvious in hindsight, but caused me a good ten minutes of head-scratching.
had the same problem, i needed to do 3 things to solve this:
disable Webdav in <modules> and <handlers>
Make sure that HttpPut is from System.Web.Http and not from System.Web.Mvc when using WebAPI
enable ExtensionlessUrlHandler like this
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
Hope this can help some of you to solve this nasty issue...
This answer fixed the issue for me. I had to add the Route attribute and the problem was solved.
[HttpPut]
[Route("")]
public HttpResponseMessage MyMethod()
You should configure it in your webservers config. It depends on the type of the webserver, where your can do it. For example by IIS, you can use a web.config file to do that in your document root. By cross-origin requests you have to add CORS headers to the response, to allow origins, methods, etc...
note: Probably you can do something about this with the ASP.NET framework as well, but I think it's unlike.
Add the following section under Handler section in web.config:
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,POST,PUT,PATCH,MERGE" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
For instance:
<handlers>
<remove name="WebDAV" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,POST,PUT,PATCH,MERGE" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
Maybe it's late now, but someone can use this.
I wanted to use PUT request and I just sent stringified object to web api and in put method only accepted that object.
JQUERY
let musterija = {
Name: name,
Email: email,
Password: password,
Username: logUser.Username,
Lastname: lastname,
GenderString: gender,
Jmbg: identification,
PhoneNumber: phone,
};
$.ajax({
method: "PUT",
url: "/api/Musterija",
data: JSON.stringify(musterija),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function () {
alert("Entity updated");
EmptyAllInputs();
$('#divprofile').show();
$('#divupdate').hide();
},
error: function (msg) {
alert("Fail - " + msg.responseText);
}
});
WEB API
[HttpPut]
public HttpResponseMessage PutMusterija(Musterija m)
I have a very basic Web API example that I constructed using the example code from this tutorial:
Code
Relevant Web.config Section
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
Route Configuration
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
View Model
public class Survey
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
ApiController
public class SurveysController : ApiController
{
public IEnumerable<Survey> All()
{
using (ITSurveyEntities model = new ITSurveyEntities())
{
return new List<Survey>(
from s in model.Surveys
select new Survey
{
Id = s.Id,
Name = s.Name,
Description = s.Description,
});
}
}
}
and it's leveraging ITSurveyEntities, which was a generated ADO.NET Entity Data Model from the database, which only contains a single table right now, Survey.
In short, I don't think I'm trying to do anything special here.
Current Result
When I try and navigate to the API using something like http://localhost:1681/api/surveys, I get a response, but the file is named surveys with no extension. Further, if I try and Save As and give it say a txt extension, the download just fails.
Expected Result
I would expect that the API would return a file names surveys.json, like the example project does with products, and the browser would ask me to open or save the file.
What I've Tried
Comparing Web.config Files
I have compared the Web.config files between my project and the example code from the tutorial that works.
Comparing Routing
I have compared the routing configuration between my project and the example code from the tutorial that works.
Excluding WebDav
I've tried to exclude WebDav because my searches have indicated that it might be the cause. I did that by modifying the Web.config in a manner that matches what's on this blog.
UPDATE 1
Okay, after the guidance by Joe Enos I found that the issue was that the view model was named Survey also and so it was throwing an error about ambiguity between the CLR type and the EDM type.
I resolved that by renaming the view model to SurveyViewModel, and the request to http://localhost:1681/api/surveys now returns a HTTP 200 and downloads the file as expected.
If you take a look at the raw request and response using Fiddler or your browser's dev tools, you should find some clues as to the problem.
The response type (xml, json, etc) will be dictated by the accepts header in your request. You didn't mention what browser you were using to call the service but I believe there is difference in the default accept header between browsers. If you only want to return only Json data from webapi try adding the following to the Global.Asax:
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Insert(0,new JsonMediaTypeFormatter());
Media Formatters are essentially how the data from the Webapi method are serialized for the browser.