Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

405 method not allowed - ASP.NET Web API

I have checked all the answers on Google and StackOverflow for 405 method not allowed in ASP.NET Web API and none of the solutions is working.

  1. Checked CORS
  2. Checked WebDAV
  3. Checked for HTTPDelete Attribute

I am creating an ASP.NET Web API and have 2 dummy controllers.

I am able to use HTTP Delete method for one controller and not the other controller.

Value Controller

using System.Collections.Generic;
using System.Web.Http;

namespace JobSite_WebAPI.Controllers
{
  public class ValuesController : ApiController
  {
    List<string> strings = new List<string>()
    {
        "value1", "value2","value3"
    };
    // GET api/values
    public IEnumerable<string> Get()
    {
        return  strings;
    }

    // GET api/values/5
    public string Get(int id)
    {
        return strings[id];
    }

    // DELETE api/values/5
    public void Delete(int id)
    {
        strings.RemoveAt(id);
    }
  }
}

Job Details Controller

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Net;
 using System.Net.Http;
 using System.Web.Http;
 using DataAccess;

 namespace JobSite_WebAPI.Controllers
 {
  public class JobDetailController : ApiController
  {
    public JobDetailController()
    {
        JobSiteEntities entities = new JobSiteEntities();
        entities.Configuration.ProxyCreationEnabled = false;
    }
    public IEnumerable<JobDetail>Get()
    {
        using (JobSiteEntities entities = new JobSiteEntities())
        {
            return entities.JobDetails.ToList();
        }
    }

[HttpGet]
    public HttpResponseMessage Get(int id)
    {
        using (JobSiteEntities entities = new JobSiteEntities())
        {
            var entity = entities.JobDetails.FirstOrDefault(e => e.JOBID == id);
            if (entity != null)
            {
                return Request.CreateResponse(HttpStatusCode.OK, entity);
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Job With Id = " + id.ToString() + " not found");
            }
        }
    }


 [HttpGet]
    public HttpResponseMessage RetrieveJobByLocation(string locationName)
    {
        try
        {
            using (JobSiteEntities entities = new JobSiteEntities())
            {
                IEnumerable<JobDetail> jobDetails = entities.JobDetails.Where(e => e.Location.LocationName.ToLower() == locationName).ToList();

                if (jobDetails != null)
                    return Request.CreateResponse(HttpStatusCode.OK, jobDetails);
                else
                    return Request.CreateResponse(HttpStatusCode.NotFound);

            }
        }
        catch(Exception ex)
        {
            return Request.CreateResponse(HttpStatusCode.BadRequest, ex);
        }
    }

    [HttpDelete]
    public HttpResponseMessage Delete(int jobId)
    {
        try
        {
            using (JobSiteEntities entities = new JobSiteEntities())
            {
                var entity = entities.JobDetails.FirstOrDefault(e => e.JOBID == jobId);

                if (entity == null)
                {
                    return Request.CreateResponse(HttpStatusCode.NotFound, "Job Id with id " + jobId + "is not found");
                }
                else
                {
                    entities.JobDetails.Remove(entity);
                    entities.SaveChanges();
                    return Request.CreateResponse(HttpStatusCode.OK);
                }
            }
        }
        catch (Exception ex)
        {
            return Request.CreateResponse(HttpStatusCode.BadRequest, ex);
        }
    }
}

}

WebAPIConfig.cs

 var cors = new EnableCorsAttribute("*", "*", "*");
 config.EnableCors(cors);


 // Web API routes
 config.MapHttpAttributeRoutes();

 config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }

        );
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/octet-stream"));

Web.Config

 <remove name="WebDAV" />
  <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" />

I have enabled CORS,disabled WebDAV.also added HTTPDelete attribute to my delete methods.

The issue am facing is for Value Controller Delete method works fine from Fiddler but for JobDetails Controller i get a 405 method not allowed.

I am able to do a GET and POST Method.Am facing the same issue with PUT too.

Added error message screenshots from fiddler. Fiddler URL Fiddler Error Message

like image 881
BSB Avatar asked Aug 26 '17 17:08

BSB


1 Answers

It works for ValuesController because the convention-based route template...

"api/{controller}/{id}" 

has {id} placeholder, which the controller conforms to, while JobDetailController.Delete(int jobId) does not match the route template because of jobId parameter name. Change those parameter arguments to int id in order for it to match route template set by the convention.

[HttpDelete]
public HttpResponseMessage Delete(int id) {
    //...
}

Otherwise you could instead use attribute routing as it is also enabled with config.MapHttpAttributeRoutes()

Reference: Attribute Routing in ASP.NET Web API 2

[RoutePrefix("api/JobDetail")] 
public class JobDetailController : ApiController {

    [HttpGet]
    [Route("")] //Matches GET api/jobdetail
    public IEnumerable<JobDetail> Get() {
        //...
    }

    [HttpGet]
    [Route("{id:int}")] //Matches GET api/jobdetail/1
    public HttpResponseMessage Get(int id) {
       //...
    }

    [HttpGet]
    [Route("{locationName}")] //Matches GET api/jobdetail/somewhere
    public HttpResponseMessage RetrieveJobByLocation(string locationName) {
        //...
    }

    [HttpDelete]
    [Route("{jobId:int}")] //Matches DELETE api/jobdetail/1
    public HttpResponseMessage Delete(int jobId) {
        //...
    }
}

Note that when routing to a controller it is either by convention or by attribute, not both. If routing by attribute to an action, then all the other actions on the controller need attribute routing as well.

like image 171
Nkosi Avatar answered Nov 17 '22 21:11

Nkosi