【问题标题】:Unable to call WebAPI Controller through AJAX, when hosted with MVC使用 MVC 托管时,无法通过 AJAX 调用 WebAPI 控制器
【发布时间】:2015-09-10 21:53:03
【问题描述】:

我有一个 MVC 应用程序,其中包含几个简单的页面,这些页面主要在 Web API 调用上运行。为简单起见,我想将它们包含在同一个项目中。我可以很好地启动并导航到我的页面,但是当我尝试通过 Ajax 调用我的 API 时,我不断收到 404 错误 - 它找不到 API 函数。

这是我的 javascript 文件:

$(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);
        }
    });
}

这是我的控制器:

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";
        }
    }
}

API 控制器位于我项目的 Controllers 文件夹中它们自己的文件夹(称为“API”)中 - 这就是名称空间在此示例控制器上包含“API”的原因。

当我在浏览器上使用 F12 来捕获发送的请求时,我可以看到我的调用包含以下信息:

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

现在我的理解是,这应该找到名称为 UserController 的 API,并找到带有 [HttpGet] 标签且不带参数的函数,然后返回字符串数组(“value1”、“value2”)。相反,它什么也没找到。

最后,这是我的路由配置(是的,它正在 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 }
            );
        }
    }

更新:

根据我目前收到的反馈,我移动了我的 Global.asax 配置。它现在看起来像这样:

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

    }

现在,当我调用我的 API 函数时,我得到...待处理。它不返回成功消息。它只是挂起。我没有得到“成功!”警告。

【问题讨论】:

  • 在 Global.asax Application_Start() 方法中注册路由的顺序是什么?如果你在 WebAPI 路由之前注册 MVC 路由,那么 api 路由将失败!
  • 通过注册MVC路由重现问题before WebApi路由。
  • 如果在 User 控制器的 Get() 方法中设置断点,它会被命中吗?
  • 是的,Get() 上的断点被击中。有趣的是,尝试下面的 codeMonkey 建议有效(这与我认为到目前为止我对 API 调用的理解相反),并且我不再收到挂起的“待处理”请求。看起来它现在工作正常?
  • 如果你支持我的评论,那么永远爱你 ;)

标签: c# ajax asp.net-mvc routing asp.net-web-api


【解决方案1】:

虽然我不确定这是否与我在评论中猜到的问题相同,但这是一个可能的原因。所以任何面临类似问题的人,这就是问题所在。

MVC 路由引擎尝试按照注册顺序将传入请求与路由匹配。

  1. 所以,如果你先注册 MVC 路由 - {controller}/{action}/{id} id:optional
  2. 然后,注册 WebAPI 路由 - api/{controller}/{id} id:可选

然后传入的请求将与 MVC 路由匹配,如果它不匹配路由 PATTERN,则只有它会与 WebAPI 路由匹配。

现在,如果您有类似api/User 的请求,它将与 MVC 路由模式匹配,并且不会与 WebAPI 路由匹配。因此,MvcHandler 将尝试创建一个ApiController MVC 控制器类并在其上调用User() 方法。结果,客户端会得到 404 - resource not found!

此外,如果您不使用属性路由,您可能需要删除/注释此行

//config.MapHttpAttributeRoutes();

为了更安全的 http-verb-to-api-method 路由,您可以在您的 api 配置中添加以下内容。

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" }) });

前两条路由可以以 [1] 纯 REST 方式调用端点,并 [2] 使用方法名称调用它们(但不符合 REST 标准!)

【讨论】:

  • 谢谢 - 这很有帮助。我移动了我的代码,现在我可以克服最初的错误,但我仍然没有得到我的“成功!”警报。请查看我的编辑以获取更多信息。
  • @user2291983 我在您的应用程序启动方法中没有看到 WebApiConfig.Register() ,我错过了什么吗?而且,您可以直接从浏览器地址栏中点击localhost:50035/api/User(如果端口号已更改,请调整端口号),看看会发生什么?
  • WebApiConfig.Register 在 GlobalConfiguration.Configure() 调用中。老实说,我不知道那是做什么的,但我所有的书都是这样做的。 URL 调用按预期工作 - 我得到了一个包含字符串数组的 JSON 对象。
  • @user2291983 好的。很好,您的功能现在正在运行。顺便说一句,您可以在 jQuery 中使用 .done() 和 .fail(),而不是 .success() 和 .error()。请参考api.jquery.com/jquery.ajax
  • 添加完整的 api 路由集,可以处理所有类型的 api 调用(纯 REST 和非 REST 方法名称)。
【解决方案2】:

试试

/api/User/Get 

而不仅仅是 /api/user。如果您想对它感兴趣,请查看[RoutePrefix] and [Route],以便您可以手动管理这些东西。

【讨论】:

  • 这行得通,但它与我认为我对 API 调用的理解背道而驰。 User 是控制器名称,我正在尝试使用 HTTP Get 请求调用不带参数的 GET 函数。
  • @user2291983 还是将其标记为正确答案? :) 我知道,MS 不太热衷于解释这些东西,尤其是当它向您展示默认路由架构时。不过就是这样。 . . /{controllerName}/{actionName}/{parameter} 或 /?parameter=XXX 。一旦您开始制作自定义 API(名称不是“GET”和“POST”),它就会更有意义。
猜你喜欢
  • 2017-11-16
  • 2015-05-17
  • 2017-01-04
  • 2010-12-21
  • 2017-06-28
  • 1970-01-01
  • 1970-01-01
  • 2017-10-24
  • 2015-05-21
相关资源
最近更新 更多