Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to call WebAPI Controller through AJAX, when hosted with MVC

I have an MVC application with a few simple pages that will mostly run on Web API calls. For simplicity I want to include them in the same project. I can launch and navigate to my page just fine, but when I try to call my API through Ajax I keep getting a 404 error - it can't find the API function.

Here is my javascript file:

$(document).ready(function () {
    //set the login button to call our test API function
    document.getElementById("login_submit").addEventListener("click", GetUser);

});

function GetUser() {
    var response = $.ajax({        
        url: '/api/User',
        method: 'GET',
        contentType: 'application/json; charset=utf-8',
        success: function (data) {
            alert("Success!");
        },
        error: function (request, status, error) {
            alert(error);
        }
    });
}

And here is my controller:

namespace MyProject.Controllers.API
{
    public class UserController : ApiController
    {
        // GET api/<controller>
        [HttpGet]        
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/<controller>/5
        [HttpGet]        
        public string Get(int id)
        {
            return "value";
        }
    }
}

The API Controllers are in their own folder (called "API") inside my Controllers folder in my project - that's why the namespace contains "API" on this example controller.

When I use F12 on the browser to capture the request getting sent, I can see that my call has the following info:

Request URL: http://localhost:50035/api/User
Request Method: GET
Status Code: 404 / Not Found

Now my understanding is that this should find the API with the name UserController and find the function with the [HttpGet] tag with no arguments, then return the string array ("value1", "value2"). Instead it doesn't find anything.

As a final note, here is my routing config (and yes, it is being initialized on Global.asax):

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

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

UPDATE:

Based on the feedback I've received so far, I moved my Global.asax configuration around. It now looks like this:

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

    }

Now when I call my API function, I get... Pending. It doesn't return a success message. It just hangs. I do not get the "Success!" alert.

like image 201
Max Avatar asked Oct 19 '22 02:10

Max


2 Answers

Though I'm not sure it's the same problem that I've guessed in my comment, but it is a probable reason. So anyone facing similar problem, this is the issue.

MVC routing engine tries to match incoming requests with routes in the same order they were registered.

  1. So, if you register MVC route first - {controller}/{action}/{id} id:optional
  2. And, then register the WebAPI route - api/{controller}/{id} id:optional

Then incoming requests will be matched with the MVC route and IF it does NOT MATCH a route PATTERN, then only it will be matched against the WebAPI route.

Now, if you have a request like api/User, it will MATCH with the MVC route pattern, and will NOT be matched against the WebAPI route. As a result, MvcHandler will try to create a ApiController MVC controller class and invoke User() method on that. As a result, client will get 404 - resource not found!

Additionally, if you are not using attribute routing, you might want to remove/comment this line

//config.MapHttpAttributeRoutes();

And for safer http-verb-to-api-method routing, you can add the following in your api config.

routes.MapHttpRoute("RestApiRoute", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" }); //this replaces your current api route
routes.MapHttpRoute("ApiWithActionRoute", "Api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
routes.MapHttpRoute("DefaultApiGetRoute", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(new string[] { "GET" }) });
routes.MapHttpRoute("DefaultApiPostRoute", "Api/{controller}", new { action = "Post" }, new { httpMethod = new HttpMethodConstraint(new string[] { "POST" }) });
routes.MapHttpRoute("DefaultApiPutRoute", "Api/{controller}", new { action = "Put" }, new { httpMethod = new HttpMethodConstraint(new string[] { "PUT" }) });
routes.MapHttpRoute("DefaultApiDeleteRoute", "Api/{controller}", new { action = "Delete" }, new { httpMethod = new HttpMethodConstraint(new string[] { "DELETE" }) });

First two routes make it possible to call the end points in [1] pure REST way & to [2] call them with method name (Not aligned to REST standards though!)

like image 192
Arghya C Avatar answered Nov 15 '22 05:11

Arghya C


Try

/api/User/Get 

instead of just /api/user. If you want to get fancy with it, check out [RoutePrefix] and [Route] so you can manage this stuff by hand.

like image 37
codeMonkey Avatar answered Nov 15 '22 06:11

codeMonkey