Search code examples
c#postjson.netasp.net-web-api-routinghttp-status-code-405

Why is my Post method disallowed?


I've been successfully using this method to GET REST data:

private JArray GetRESTData(string uri)
{
    try
    {
        var webRequest = (HttpWebRequest)WebRequest.Create(uri);
        var webResponse = (HttpWebResponse)webRequest.GetResponse();
        var reader = new StreamReader(webResponse.GetResponseStream());
        string s = reader.ReadToEnd();
        return JsonConvert.DeserializeObject<JArray>(s);
    }
    catch // This method crashes if only one json "record" is found - try this:
    {
        try
        {
            MessageBox.Show(GetScalarVal(uri));
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
    return null;
}

Between the webRequest and webResponse assignments, I added this:

if (uri.Contains("Post"))
{
    webRequest.Method = "POST";
}

...and called it with this URI:

http://localhost:28642/api/Departments/PostDepartment/42/76TrombonesLedTheZeppelin

Although I have a Post method that corresponds to it:

Controller

[Route("api/Departments/PostDepartment/{accountid}/{name}/{dbContext=03}")]
public void PostDepartment(string accountid, string name, string dbContext)
{
    _deptsRepository.PostDepartment(accountid, name, dbContext);
}

Repository

public Department PostDepartment(string accountid, string name, string dbContext)
{
    int maxId = departments.Max(d => d.Id);
    // Add to the in-memory generic list:
    var dept = new Department {Id = maxId + 1, AccountId = accountid, Name = name};
    departments.Add(dept);
    // Add to the "database":
    AddRecordToMSAccess(dept.AccountId, dept.Name, dbContext);
    return dept;
}

...it fails with, "The remote server returned an error: (405) Method Not Allowed."

Why is it not allowed?

UPDATE

Based on what I found here: http://blog.codelab.co.nz/2013/04/29/405-method-not-allowed-using-asp-net-web-api/, I added this to Web.Config:

  <validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="WebDAVModule" />
    </modules>
    <handlers>
      <remove name="WebDAV" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT" 
type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" 
preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>

...so that it went from this:

<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></configuration>

...to this:

<system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="WebDAVModule" />
    </modules>
    <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,HEAD,POST,DEBUG,PUT" 
type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" 
preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
</system.webServer></configuration>

....but it made no diff.

UPDATE 2

It doesn't even make it to the call in the Controller:

        [HttpPost]
[System.Web.Http.Route("api/Departments/PostDepartment/{accountid}/{name}/{dbContext=03}")]
        public void PostDepartment(string accountid, string name, string dbContext)
        {
            _deptsRepository.PostDepartment(accountid, name, dbContext);
        }

I have a breakpoint inside it that's not reached...???

UPDATE 3

VirtualBlackFox's last comment was the one that did the trick. I just changed my "is it a post?" in my client code to the following:

if (uri.Contains("Post"))
{
    webRequest.Method = "POST";
    webRequest.ContentLength = 0; // <-- This line is all I added
}

...and now it works.


Solution

  • I don't do Asp.Net but i'll guess that you need to specify the HttpPost attribute as can be seen in Attribute Routing / HTTP Methods documentation :

    [HttpPost]
    [Route("api/Departments/PostDepartment/{accountid}/{name}/{dbContext=03}")]
    public void PostDepartment(string accountid, string name, string dbContext)
    {
        _deptsRepository.PostDepartment(accountid, name, dbContext);
    }
    

    Small sample that work on my PC :

    TestController.cs:

    using System.Web.Http;
    
    namespace WebApplication2.Controllers
    {
        public class TestController : ApiController
        {
            [HttpPost]
            [Route("api/Departments/PostDepartment/{accountid}/{name}/{dbContext=03}")]
            public string PostDepartment(string accountid, string name, string dbContext)
            {
                return accountid + name + dbContext;
            }
        }
    }
    

    Test.html :

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script>
        <script>
            $(function () {
                $.ajax("api/Departments/PostDepartment/accountid/name/dbContext", {
                    type: 'POST', success: function (data) {
                        $('#dest').text(data);
                    }
                });
            });
        </script>
    </head>
    <body>
        <div id="dest"></div>
    </body>
    </html>
    

    Sample program to call the service in C# :

    namespace ConsoleApplication1
    {
        using System;
        using System.IO;
        using System.Net;
        using Newtonsoft.Json;
    
        class Program
        {
            static void Main()
            {
                Console.WriteLine(GetRestData(@"http://localhost:52833//api/Departments/PostDepartment/42/76TrombonesLedTheZeppelin"));
                Console.ReadLine();
            }
    
            private static dynamic GetRestData(string uri)
            {
    
                var webRequest = (HttpWebRequest)WebRequest.Create(uri);
                webRequest.Method = "POST";
                webRequest.ContentLength = 0;
                var webResponse = (HttpWebResponse)webRequest.GetResponse();
                var reader = new StreamReader(webResponse.GetResponseStream());
                string s = reader.ReadToEnd();
                return JsonConvert.DeserializeObject<dynamic>(s);
    
            }
        }
    }