2

I've looked at the many similar issues posted but couldn't find a solution that worked for me. So the call to Get is working fine but call to POST returns 404. I created a simple WebAPI project (MVC 4).

public class CasesController : ApiController
{
    [Inject]
    public ICaseManager CaseManager { get; set; }

    // GET api/cases
    public IEnumerable<Case> Get()
    {
        return CaseManager.ListCases();
    }

    // POST api/cases
    [HttpPost]
    public void Post([FromBody]Case objCase)
    {

    }
}

So when I navigate to http://localhost:34645/api/cases I get the following:

[{"CaseID":1,"CaseCode":"one","CaseDescription":"case one"},{"CaseID":2,"CaseCode":"two","CaseDescription":"case two"}]

I created another project (ASP.Net) and have an html file within it with the following code:

<script src="Scripts/jquery-2.0.3.js"></script>
<script src="Scripts/jquery-2.0.3.intellisense.js"></script>

<script type="text/javascript">
    function postData() {
        $.post('http://localhost:34645/api/cases', { "CaseID": 3, "CaseCode": "three", "CaseDescription": "case three" }).done(function (data) { alert("Success " + data); }).fail(function (xhr, textStatus, errorThrown) { alert("Error " + xhr.status); });
    }
</script>

Every time I click the button that invokes postData, I get an alert "Error 404".

Here are my routes:

Global.asax:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
}

WebAPIConfig.Register:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        //RA: to get JSON
        var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
        config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);

    }
}

RouteConfig:

 public class RouteConfig
 {
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

Please advise.

Spock
  • 7,009
  • 1
  • 41
  • 60
Raza Ali
  • 595
  • 1
  • 13
  • 29

5 Answers5

5

Be careful about the order of the WebApi registration line. I found when I specifically had the Global.asax.cs code in this order it worked:

        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

Otherwise, it failed with 404 error.

ossentoo
  • 1,675
  • 2
  • 20
  • 44
  • That was it for me. And there doesn't seem to be a diff between WebApiConfig.Register(GlobalConfiguration.Configuration) and GlobalConfiguration.Configure(WebApiConfig.Register). But order matters here. – wes Dec 30 '15 at 22:40
0

If these are two separate solutions, check they're both running - it's possible that they're trying to share a server instance, so the WebAPI you're trying to hit isn't running when the other app is. If they're projects within the same solution, check that they're both set to run on startup, or again, the WebAPI won't be running when the ASP.NET project tries to access it.

anaximander
  • 7,083
  • 3
  • 44
  • 62
  • Both are running and are set as startup in the solution. Solution is one, projects are separate. Is there a way I can get a more verbose error? Thx – Raza Ali Dec 04 '13 at 09:36
  • Ok, this is strange: I placed a breakpoint on the POST action and it did get called! The objCase is also populating correctly. Strangely enough, the "fail" gets called in the postData function and not the "done" part. I tried returning HttpResponseMessage (Created) but same issue. – Raza Ali Dec 04 '13 at 09:46
0

Try below. It works for me. I have removed some properties for brevity.

public class CasesController : ApiController {
    // GET api/cases
    public IEnumerable<Case> Get() {
        var caseManager = new CaseManager();
        return caseManager.ListCases();
    }

    // POST api/cases
    [HttpPost]
    public string Post([FromBody]Case objCase) {
        return objCase.CaseName;
    }
}

public interface ICaseManager     {
    IEnumerable<Case> ListCases();
}

public class CaseManager     {
    public IEnumerable<Case> ListCases()
    {
        return new List<Case>() { new Case() { CaseID = 1, CaseName = "one" } };
    }
}

public class Case     {
    public int CaseID { get; set; }
    public string CaseName { get; set; }
}

View

<script type="text/javascript">
    //function postData() {
    //    $.post('http://localhost:58820/api/cases', { "CaseID": 3, "CaseCode": "three", "CaseDescription": "case three" })
    //        .done(function (data) { alert("Success " + data); }).fail(function (xhr, textStatus, errorThrown)
    //        { alert("Error " + xhr.status); });
    //}
$(document).ready(function () {
    $('#save-source').click(function (e) {

        e.preventDefault();
        var source = {
            'ID': 0,
            'CaseID': 3,
            'CaseName': "three",
        };
        $.ajax({
            type: "POST",
            dataType: "json",
            url: "/api/cases",
            data: source,
            success: function (data) {
                alert(data);
            },
            error: function (error) {
                jsonValue = jQuery.parseJSON(error.responseText);                    
            }
        });
    });
});


</script>

 @using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm"}))
    {
        <input type="submit" id="save-source" name="save-source" value="Add" />
    }
Spock
  • 7,009
  • 1
  • 41
  • 60
  • Thanks but I'm not sure how this is different from $.post. Anyways, I did try this but it still lands in the error function. Debugger shows post gets called properly. I placed an alert(error.responseText) as well as alert(jsonValue). The former is undefined and the latter doesn't alert at all. So still same problem. – Raza Ali Dec 04 '13 at 10:34
  • You must be doing something different. If I just copy paste the above solution in a brand new WebAPI hello world app, it just works. I suggest try to implement exactly the way I have done see what happens. If it still of an issue, you can start removing any other component that might interfere and see what makes the difference. If you still can't figure it out. Create a brand new WebAPI (that comes with template), and try to get it working there first. I'm sure you are not far away from solving this issue. – Spock Dec 04 '13 at 10:48
  • So here is the issue: When I moved the html page to the same project as the webapi and gave the relative reference /api/cases it works fine. But when its a separate ASP.Net project I am getting the error that I mentioned. So yes it's getting closer but still not there! Thanks for your assistance. – Raza Ali Dec 04 '13 at 11:13
  • I think when you try to make an ajax api call from another asp.net project, you might hit a cross-browser access issue. Here is a post how you would solve that issue. http://stackoverflow.com/questions/20035101/no-access-control-allow-origin-header-is-present-on-the-requested-resource-w – Spock Dec 04 '13 at 11:35
  • Ok, so it was JSONP that I needed. I installed the NuGet package WebApiContrib.Formatting.Jsonp and added the jsonp formatter in global.asax. So even $.post is now working with the datatype jsonp. Thanks for your support. – Raza Ali Dec 04 '13 at 11:46
  • Oops, spoken too soon. It is going into success alright but not calling POST. Instead it is going to the Get action even though it's a POST call. Need more R&D about JSONP i guess. Or may be CORS is the answer. Let's see. – Raza Ali Dec 04 '13 at 12:20
0

After different attempts, this article helped me the most:

WebAPI and CORS enabled REST services

I also installed the Ninject WebApi DependencyResolver package through NuGet.

Raza Ali
  • 595
  • 1
  • 13
  • 29
0

You write that you post to $.post('http://localhost:34645/api/cases'...

Either you change the url to include the action method name explicitly, like: $.post('http://localhost:34645/api/cases/post'.. or you add in your config.Routes.MapHttpRoute a default action which will be used when none action specified in the url

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

OR you can change your route to

  config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",               
            ); 

(without {action} and then web api will reach the Post method when you use a post http verb (it knows to do it automatically, but if you set a default action it'll override it)

BornToCode
  • 9,495
  • 9
  • 66
  • 83