【问题标题】:Calling WebAPI Service in ASP.NET MVC 5 application, using jQuery使用 jQuery 在 ASP.NET MVC 5 应用程序中调用 Web API 服务
【发布时间】:2015-01-22 08:00:26
【问题描述】:

我正在尝试构建一个简单的 WebApi 服务,该服务将实时返回帖子的 cmets。所以该服务只实现了 Get 方法,它是一个简单的返回 IEnumerable 字符串用于传递的 Post ID:

 public class CommentApiController : ApiController
    {
        // GET api/<controller>
        public IEnumerable<string> Get(int id)
        {
            return new ApplicationDbContext().Posts.First(x => x.PostId == id).CommentId.Select(x => x.CommentText).ToList();
        }

        // POST api/<controller>
        public void Post([FromBody]string value)
        {
        }

        // PUT api/<controller>/5
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE api/<controller>/5
        public void Delete(int id)
        {
        }
    }

我也制作了 WebApiConfig 类并指定路由如下:

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

在我的 Global.asax.cs 文件中,我为该路由添加了参考:

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

在简单的局部视图中,我尝试每 8 秒调用一次此服务,因此帖子的 cmets 可以自己出现,无需刷新页面,以检查是否有其他用户对帖子发表了评论。

@model List<StudentBookProject.Models.Post>

<table class="table table-striped">
    @foreach (var item in Model)
    {
        if (item.ImagePost != null)
        {
            <tr class="info">
                <td>@item.CurrentDate</td>
            </tr>
            <tr class="info">
                <td>
                    |@Html.ActionLink("Delete", "Delete", new { id = item.PostId }) |
                    @Html.ActionLink("Comment", "AddComment", new { id = item.PostId }, new { @class = "comment" }) |
                </td>
            </tr>
            <tr class="info">
                <td>
                    <img src="data:image/png;base64,@Convert.ToBase64String(item.ImagePost, 0, item.ImagePost.Length)" width="620" />
                </td>
            </tr>
            <tr>
                <td>
                    @Html.Partial("~/Views/Posts/ListComment.cshtml", item.CommentId)
                </td>
            </tr>
        }
        if (item.FilePost != null)
        {
            <tr class="info">
                <td>@item.CurrentDate</td>
            </tr>
            <tr class="info">
                <td>
                    | @Html.ActionLink("Delete", "Delete", new { id = item.PostId }) |
                    @Html.ActionLink("Comment", "AddComment", new { id = item.PostId }, new { @class = "comment" }) |
                </td>
            </tr>
            <tr class="info">
                <td>
                    File attachment
                </td>
            </tr>
        }
        if (item.TextPost != "")
        {
            <tr class="info">
                <td>@item.CurrentDate</td>
            </tr>
            <tr class="info">
                <td>
                    | @Html.ActionLink("Edit", "Edit", new { id = item.PostId }, new { @class = "lnkEdit" }) |
                    @Html.ActionLink("Delete", "Delete", new { id = item.PostId }) |
                    @Html.ActionLink("Comment", "AddComment", new { id = item.PostId }, new { @class = "comment" }) |
                </td>
            </tr>
            <tr class="info">
                <td>
                    @item.TextPost
                </td>
            </tr>
        }
    }
</table>

@{
    int i = 0;

    while (i < Model.Count())
    {
        if (Model[i].ImagePost != null || Model[i].TextPost != null || Model[i].FilePost != null)
        {
            <script>
                $(document).ready(function () {

                    var showAllComments = function () {                 
                        $.ajax({
                            url: "/api/CommentApi/" + "@Model[i].PostId.ToString()"        
                        }).done(function (data) {
                            var html = "";                              
                            for (item in data) {
                                html += '<div>' + data[item] + '</div>';
                            }
                            var divID = "@("quote" + Model[i].PostId.ToString())"

                            $('#' + divID).html(html);

                            setInterval(function () {   
                                showAllComments();                                          
                            }, 8000);                                                       

                        });
                    };
                })
            </script>
        }
        ++i;
    }
}

我的服务没有被调用,至少没有以正确的方式调用,导致新的 cmets 仅在刷新页面后才会显示。我知道 WebApi,尤其是在这种情况下,应该很容易实现并且非常简单,但我对这项技术完全陌生,我不知道我错过了什么或错误地实现了什么。 一直在尝试找到一些合适的教程来帮助我解决这个问题,但还没有任何帮助。

我在某处读到我应该为 WebDAV 在 Web.config 文件中添加一行,但这对我也没有帮助。

<handlers>
      <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" />
</handlers>

是否有人看到我没有做的事情以及可能的错误在哪里?另外,有人知道一些好的 .NET WebApi 教程吗?

附:当我使用以下命令直接从浏览器调用 WebApi Get 方法时:.../api/CommentApi/id 路由服务被调用并返回 cmets 以获取传递的帖子 id,所以服务没问题,我没有在代码很好......

【问题讨论】:

  • 您是否收到任何脚本错误?您是否尝试过在浏览器开发者工具中查看请求返回的内容?
  • @jgauffin 我什么都没有……这真的很奇怪……我先尝试使用浏览器控制台,然后再使用 Fiddler……当我设置断点时,它永远不会到达那部分代码...我想我没有做好一些映射...
  • 该页面是通过 ajax 请求生成的吗?如果是这种情况,将永远不会调用 document.ready 回调。尝试从页面中删除它(直接执行脚本)。
  • 你使用哪个 jQuery?你看过这个网站吗? link
  • @Marcin 我已经尝试过该示例中的版本,但仍然没有...我之前使用的是旧版本...

标签: c# jquery asp.net-mvc-5 asp.net-web-api


【解决方案1】:

首先,正如您自己所说,当您在浏览器中键入 URL 并按 Enter 时,您会收到来自 Web API 的响应:这意味着 Web API 服务和服务器已正确配置。所以你的问题与配置无关:问题出在请求本身。

网址和方法

要使请求正常工作,它必须是一个 GET 请求,指向正确的 URL,并带有正确的参数。

您的路线模板看起来像这样routeTemplate: "api/{controller}/{id}" 和可选的id。你的方法是一个get方法。在这种方法中,参数可以从路由(模板中的{id}段)或查询字符串中恢复,即:

  • GET api/CommentApi/5
  • GET api/CommentApi?id=5

您可以使用这些网址中的任何一个。

返回的数据格式和Accept header

Web API 可以返回两种不同格式的数据,XML 或 JSON。如果您不指定任何内容,则返回的数据采用 XML 格式(这是您在浏览器中键入 URL 时得到的)。如果你更喜欢 JSON,那就是 在这种情况下,您需要在请求中添加标头:Accept: application/json

注意:指定 Content-Type 是没有意义的,因为您不能在 GET 请求中发送有效负载(正文中的数据)

用 jQuery 实现

从 Web API 服务中的 GET 方法获取数据的最简单方法是使用 jQuery .getJSON。如果您查看文档,您会发现此方法等效于

$.ajax({
  dataType: "json",
  url: url,
  data: data,
  success: success
});

而且,如果您阅读了.ajax() 的文档,您就会明白指定dataType: "json" 等同于包含标题Accept:application/json,它要求Web API 返回JSON 数据。您还将了解默认方法是 GET。因此,您必须确保的唯一另一件事是 URL 看起来符合预期。看getJSON的签名:jQuery.getJSON( url [, data ] [, success ] )

它需要一个 URL 和可选的数据和一个成功回调。我建议不要使用回调,所以让我们看看 2 个可能的选项来发出请求:

  • $.getJSON('/api/CommentApi/'+id) 将呈现类似 /api/CommentApi/5 的 URL(未指定数据)
  • $.getJSON('/api/CommentApi',{id:5}) 将呈现类似 /api/CommentApi?id=5 的 URL(指定为 JavaScript 对象的数据,属性名称类似于操作的参数名称:在本例中为 id)

使用响应

我建议不要使用success 回调,而是使用promise .done 方法。因此,无论您使用哪种语法进行调用,都必须像这样推迟.done(就像您在原始代码中所做的那样):

$.getJSON('/api/CommentApi/'+id).done(function(response) {
    // use here the response
});

最终代码

所以,修改后的showAllComments 方法看起来像这样

请特别注意cmets

var showAllComments = function () {
    $.getJSON("/api/CommentApi/" + "@Model[i].PostId.ToString()")
    .done(function (data) {
        // create empty jQuery div
        var $html = $('<div>'); 
        // Fill the empty div with inner divs with the returned data
        for (item in data) {
          // using .text() is safer: if you include special symbols
          // like < or > using .html() would break the HTML of the page
          $html.append($('<div>').text(data[item]));
        }
        // Transfer the inner divs to the container div
        var divID = "@("quote" + Model[i].PostId.ToString())";
        $('#' + divID).html($html.children());
        // recursive call to the same function, to keep refreshing
        // you can refer to it by name, don't need to call it in a closure
        // IMPORTANT: use setTimeout, not setInterval, as
        // setInterval would call the function every 8 seconds!!
        // So, in each execution of this method you'd bee asking to repeat
        // the query every 8 seconds, and would get plenty of requests!!
        // setTimeout calls it 8 seconds after getting each response,
        // only once!!
        setTimeout(showAllComments, 8000);                                        
    }).fail(function() {
        // in case the request fails, do also trigger it again in seconds!
        setTimeout(showAllComments, 8000);                                        
    });
};

【讨论】:

  • 哇这么好的解释,非常感谢你!但是,不管你信不信,代码不能正常工作......它不会像假设的那样每 8 秒调用一次服务......而且很难追踪为什么......:-/ @JotaBe
  • 那么我们必须追查问题:请打开 Chrome,按 F12 并执行以下两件事:1) 转到 Sources,选择该脚本所在的文件,然后在功能代码,以便您可以查看代码是否到达。 2) 转到网络选项卡并查找应用程序发出的请求。你应该找到失败的请求,如果你检查它们,也许你可以给我更多的信息。并回答这个问题:Web 应用程序发出的请求是否是由 Wep API 正在处理的不同域、机器或端口中的?我们会做对的,别担心!
  • 最后一天做了并听到这个...我在 Web.config 文件中进行了一些更改,在根本没有调用服务并且在浏览器中没有显示任何内容之后我已经设法使它调用服务,HTTP 状态正常,当我检查每个调用时,它返回 JSON 中的有效数据......但它仍然没有在页面上打印结果......所以我想问题出在 View now ...并且 Web Api 与应用程序的其余部分位于同一端口上 :) @JotaBe
  • 那你就得利用Chrome调试控制台来判断是什么问题了。我敢打赌你找错了id 来找到你的 div。按 F12 打开 Chrome 调试工具。转到源选项卡,在行$('#'+divId)... 上放置一个断点。命中断点后,转到调试器控制台(如果未打开,请按 Esc)并键入 divId + enter。检查具有此 id 的 div 是否真的存在于您的 html 中。如果看起来确实如此,请输入$('#'+divId) 并按回车键。您应该会看到一个包含元素的数组,即搜索到的 div。如果缺少,请更正代码。
【解决方案2】:

您需要设置dataType、contentType并在数据中传递id参数;

尝试以下方法:

 $.ajax({
        url: "/api/CommentApi/Get",
        type: 'GET',
        data: { id: @Model[i].PostId },
        dataType: 'json',
        contentType: 'application/json',
        success: function (data) { 

                            var html = "";                              
                            for (item in data) {
                                html += '<div>' + data[item] + '</div>';
                            }
                            var divID = "@("quote" + Model[i].PostId.ToString())"

                            $('#' + divID).html(html);

                            setInterval(function () {   
                                showAllComments();                                          
                            }, 8000);                                                        }
    });

更新

好的,将其分解为一个更简单的工作示例,以下将起作用。

javascript

    var urlString = "http://localhost/api/CommentApi/Get";

    $.ajax({
        url: urlString,
        type: 'GET',
        data: { id : 1},
        dataType: 'json',
        contentType: 'application/json',
        success: function (data) { console.log(data); }
    });
</script>

让您的控制器只返回硬编码值,如下所示:

// GET api/values
public IEnumerable<string> Get(int id)
{
    return new List<string>() { "one", "two" };
}

运行上述程序并测试它是否将值打印到控制台。

【讨论】:

  • 刚试过...没用...奇怪的是根本不调用服务...也许url应该不同? @hutchonoid
  • @nemo_87 我已将数据更改为data: { id: @Model[i].PostId },请尝试一下。我想它会认为它是一个字符串。
  • 不,没用 :( 感谢您尝试帮助我...我完全不敢相信这个简单的事情会如此复杂...@hutchonoid
  • @nemo_87 应该不会,会在测试项目中尝试。
  • @nemo_87 尝试更新,它将它与剃刀循环等隔离开来,只是为了看到它被调用了。 ps你需要在url中添加get。
【解决方案3】:

您好,起初看起来您没有调用该方法。我找不到任何“showAllComments”函数的调用。当我复制您的代码并在声明后调用时

 <script>
    $(document).ready(function () {

        var showAllComments = function () {
            // code
        };

        showAllComments();
    })
</script>

我看到调用了 ApiController 方法,为我更新了 html。您确定调用了“showAllComments”函数吗?

而且 JotaBe 的回答非常好,应该可以。并遵循 JotaBe 所写的 setTimeout 函数。 见功能说明。

http://www.w3schools.com/jsref/met_win_setinterval.asp http://www.w3schools.com/jsref/met_win_settimeout.asp

【讨论】:

    【解决方案4】:

    您是否查看了呼叫的路由以及真正的服务器响应是什么?

    您可能希望将 Route 属性设置到操作上,然后查看使用 Postman 快速调用它以查看响应。我发现如果不使用邮递员,检查路由比需要的要困难得多。

    示例项目似乎只是将根称为 /api/cmets/1 或 api/cmets/?id=1 其中 get 调用看起来是 /api/cmets/get?id=1 从路由作为 IIRC默认路由的操作为 Index not get or view。

    稍后当我可以启动一个演示项目并测试发生了什么时,我会更详细地了解这一点

    编辑:如果您将属性添加到控制器/动作,您可以更轻松地看到调用的构造方式:

    [RoutePrefix("api/Account")]
    public class AccountController : BaseAPIController
    {
           [Route("someroute")]
           [HttpGet]
           public int mymethod(int ID){return 1;}
    }
    

    在此示例中,您将调用 get 到 /api/account/soumeroute?id=123123 并返回 1。 HTTPGet 只是在可读性上分离代码的一个帮助(如果你启用了它,还有 XML API 文档,这是一个很好的选择)

    【讨论】:

    • 你的意思是像在Get方法上面定义路由?我没有尝试过...但我可以尝试...仍然我认为真正的问题在于 ajax 调用...但这只是一个猜测...谢谢您的建议...你发现以后我会非常感激的:) @finman
    【解决方案5】:

    t您是否尝试添加此内容:

    dataType: 'json',
    async: true,
    type: 'GET', //EDIT: my mistake should be GET, thx JotaBe for pointing that
    

    你的 $.ajax 调用?

    或者试试这个方法:

    $.ajax({
                    url: "/api/CommentApi/" + "@Model[i].PostId.ToString()",
    
                    dataType: 'json',
                    async: true,
                    type: 'GET',
                    error: function () {
                        alert('Error');
                    }
    
                success: function (data) {
                    alert('Success')
                }});
    

    【讨论】:

    • 不...不工作:( Ajax 根本不调用服务...所以我猜错误在 url 中或者调用函数可能不好...
    • 嗯? type: 'POST' 获取方法?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    • 1970-01-01
    • 2016-11-08
    • 1970-01-01
    相关资源
    最近更新 更多