Search code examples
c#httpasp.net-web-api2postman

Error "405 Method Not Allow" When Calling Put method in Postman with body parameter


I was trying to call the Put method through Postman and always getting error:
"405 Method Not Allow" and "Message": "The requested resource does not support http method 'PUT'."

I'm using DocumentDB and C#. Here is my code:

[Route("multilanguage/Resources/{id}/{Language}")]
[HttpPut]
public async Task<IHttpActionResult> UpdateResource(string Id, string Language, string text)
{
    client = new DocumentClient(new Uri(EndPoint), AuthKey);
    var collectionLink = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);

    var query = new SqlQuerySpec("SELECT * FROM MultiLanguage as m where m.id = @pmId", 
    new SqlParameterCollection(new SqlParameter[] { new SqlParameter { Name = "@pmId", Value = Id } }));

    Document doc = client.CreateDocumentQuery<Document>(
            collectionLink, query).AsEnumerable().FirstOrDefault();

    List<Models.Translations> d = doc.GetPropertyValue<List<Models.Translations>>("Translations");
    Models.Translations temp = d.Find(p => p.Language == Language);

    temp.Content = text;
    temp.LastModified = DateTimeOffset.Now;
    temp.ModifiedBy = "admin";
    doc.SetPropertyValue("Translations", d);

    Document updated = await client.ReplaceDocumentAsync(doc);

    return Ok();
}

When I call the Put method throught Postman, I call "http://localhost:XXXX/multilanguage/resources/2/En". "2" and "En" are the first two parameters in my code. And I also specify the "text" parameter value in the Postman request Body with x-www-form-urlencoded type: key = text, value = Test! This put method suppose to update the temp.Content value to "Test!". However, it always failed with the error I mentioned above.

Did I miss anything here?


Solution

  • The 405 error when performing a PUT request to web api is a well known topic. You can find many solutions in this or this SO question.

    And for the design of you controller:

    PUT are designed to have a body, just like POST and in your case you should send all parameters in the body instead.

    You should create a class which contains the objects you want to send to the server:

    public class resourceClass
    {
        public string Id { get; set; }
        public string Language { get; set; }
        public string text { get; set; }
    }
    

    Then specify the route without the attribute routing and get the object from the request body

    [Route("multilanguage/Resources/PutResource")]
    [HttpPut]
    public async Task<IHttpActionResult> UpdateResource([FromBody] resourceClass obj)
    {
        client = new DocumentClient(new Uri(EndPoint), AuthKey);
        var collectionLink = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
    
        var query = new SqlQuerySpec("SELECT * FROM MultiLanguage as m where m.id = @pmId", 
        new SqlParameterCollection(new SqlParameter[] { new SqlParameter { Name = "@pmId", Value = Id } }));
    
        Document doc = client.CreateDocumentQuery<Document>(
                collectionLink, query).AsEnumerable().FirstOrDefault();
    
        List<Models.Translations> d = doc.GetPropertyValue<List<Models.Translations>>("Translations");
        Models.Translations temp = d.Find(p => p.Language == Language);
    
        temp.Content = text;
        temp.LastModified = DateTimeOffset.Now;
        temp.ModifiedBy = "admin";
        doc.SetPropertyValue("Translations", d);
    
        Document updated = await client.ReplaceDocumentAsync(doc);
    
        return Ok();
    }
    

    From the client you could add an object to the PUT request of Content-Type application/json like this

    var data = {
        Id: clientId,
        Language: clientLanguage,
        text: clientText
    };
    

    Don't forget to stringify the json when adding it to the http request

    data: JSON.stringify(data),
    

    The PUT controller will then be reached at "http://localhost:XXXX/multilanguage/resources/putresource".