简体   繁体   English

使用jQuery在ASP.NET MVC 5应用程序中调用WebAPI服务

[英]Calling WebAPI Service in ASP.NET MVC 5 application, using jQuery

I'm trying to build a simple WebApi service that will return comments for posts in real time. 我正在尝试构建一个简单的WebApi服务,该服务将实时返回帖子的评论。 So the service only have Get method implemented and it's a simple returning IEnumerable of strings for passed Post ID: 因此,该服务仅实现了Get方法,并且它是传递的Post ID的字符串的IEnumerable的简单返回:

 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)
        {
        }
    }

I've made WebApiConfig class too and specified routing as following: 我也制作了WebApiConfig类,并指定了以下路由:

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

In my Global.asax.cs file I've added a reference for that routing: 在我的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);
}

In simple partial view I'm trying to call this service every 8 seconds, so the comments for posts can appear by themselves, without need for refreshing the page, to check if some other users posted a comment on a post. 在简单的局部视图中,我试图每8秒调用一次此服务,以便帖子的评论可以自己显示,而无需刷新页面,从而检查其他用户是否在帖子中发表了评论。

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

My service is not called, at least not in a proper way, cause new comments shows only after refreshing a page. 没有调用我的服务,至少没有以正确的方式调用它,导致仅在刷新页面后才会显示新评论。 I know that WebApi, especially in this case should be easy to implement and pretty straight forward, but I'm totally new to this technology and I don't have a clue what I've missed or implemented wrongly. 我知道WebApi,尤其是在这种情况下,应该易于实现并且非常简单,但是我对这项技术是完全陌生的,我不知道我错过或错误实现了什么。 Been trying to find some fitting tutorial to help me to solve this problem, but nothing helped me yet. 一直试图找到一些合适的教程来帮助我解决这个问题,但是还没有任何帮助。

I've read somewhere that I've should add one line to Web.config file for WebDAV, but that didn't helped me also. 我读过某个地方,应该为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>

Does someone sees what I've didn't done and where is possible mistake? 有人看到我没做过的事情以及可能出现的错误吗? Also, does someone knows some good .NET WebApi tutorial? 另外,有人知道一些不错的.NET WebApi教程吗?

PS When I made a call to a WebApi Get method directly from browser using: .../api/CommentApi/id route services gets invoked and returns comments for passed id of post, so service is ok, I'm not calling it in a code in a good way... PS当我直接使用以下方法从浏览器中调用WebApi Get方法时:... / api / CommentApi / id route服务被调用并返回传递的post id的注释,因此该服务还可以,我没有在其中调用它很好的代码...

First of all, as you yourself say, when you type the URL in a browser and press enter you get a response from the Web API: that means that the Web API service, and the server are correcty configured. 首先,就像您自己说的那样,当您在浏览器中键入URL并按Enter键时,您会收到来自Web API的响应:这意味着Web API服务和服务器已正确配置。 So your problem has nothing to do with configuration: the problem is in the request itself. 因此,您的问题与配置无关:问题在于请求本身。

URL and method 网址和方法

For the request to work it must be a GET request, to the correct URL, and with the correct parameters. 为了使请求生效,它必须是GET请求,正确的URL和正确的参数。

Your route template looks like this routeTemplate: "api/{controller}/{id}" with optional id . 您的路由模板看起来像如下routeTemplate: "api/{controller}/{id}"带有可选的id Your method is a get method. 您的方法是一种get方法。 In this kind of method the parameter can be recovered from either the route ( {id} segment in the template) or the query string, ie: 在这种方法中,可以从路由(模板中的{id}段)或查询字符串中恢复参数,即:

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

You can use any of this URLs. 您可以使用任何一个URL。

Returned data format, and Accept header 返回的数据格式和Accept标头

The Web API can return the data in two different formats, XML or JSON. Web API可以两种不同的格式(XML或JSON)返回数据。 If you don't specify anything, the returned data comes in XML format (that's what you get you get when you type the URL in your browser). 如果未指定任何内容,则返回的数据将采用XML格式(这就是在浏览器中键入URL时得到的信息)。 If you prefer JSON, which is the case, you need to add a header to your request: Accept: application/json 如果您喜欢JSON(在这种情况下),则需要在请求中添加标头: Accept: application/json

NOTE: it makes no sense to specify a Content-Type because you cannot send payload (data in the body) in a GET request 注意:指定Content-Type没有意义,因为您无法在GET请求中发送有效载荷(主体中的数据)

Doing it with jQuery 用jQuery做到这一点

The easiest way to get data from a GET method in the Web API service, is using jQuery .getJSON . 从Web API服务中的GET方法获取数据的最简单方法是使用jQuery .getJSON If you look at the docs you'll see that this method is equivalent to 如果您查看文档,您会发现此方法等效于

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

And, if you read the docs for .ajax() , you'll understand that specifying dataType: "json" is equivalent to including the header Accept:application/json , which asks the Web API to return JSON data. 而且,如果您阅读了.ajax()的文档,您将了解指定dataType: "json"等同于包含标头Accept:application/json ,该标头要求Web API返回JSON数据。 And you'll also understand that the default method is GET. 您还将了解默认方法是GET。 So the only other thing that you must ensure is that the URL looks as expected. 因此,您必须确保的唯一一件事就是URL看起来像预期的那样。 Look at the signature of getJSON : jQuery.getJSON( url [, data ] [, success ] ) 查看getJSON的签名: jQuery.getJSON( url [, data ] [, success ] )

It requires an URL and optionally data and a succes callback. 它需要一个URL和可选的数据以及一个成功的回调。 I recommend not using the callback, so let's see 2 possible options to make the request: 我建议不要使用回调,因此让我们来看两个发出请求的可能选项:

  • $.getJSON('/api/CommentApi/'+id) which would render an URL like /api/CommentApi/5 (data not specified) $.getJSON('/api/CommentApi/'+id)会呈现类似/api/CommentApi/5的URL(未指定数据)
  • $.getJSON('/api/CommentApi',{id:5}) which would render an URL like /api/CommentApi?id=5 (data specified as JavaScript object, with property names like the action's parameter names: id in this case) $.getJSON('/api/CommentApi',{id:5})会呈现类似/api/CommentApi?id=5的URL(指定为JavaScript对象的数据,其属性名称类似于操作的参数名称: id in this案件)

Using the response 使用回应

I recommend not to use the success callback, and use the promise .done method instead. 我建议不要使用success回调,而应使用promise .done方法。 So, whichever syntax you use for the call, you must postpone the .done like this (as you did in your original code): 因此,无论用于调用的哪种语法,都必须像这样在.done推迟(就像您在原始代码中所做的那样):

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

The final code 最终代码

So, the modified showAllComments method would look like this 因此,修改后的showAllComments方法将如下所示

Please, pay special attention to the comments 请特别注意评论

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

You need to set the dataType , contentType and also pass the id parameter across in the data; 您需要设置dataTypecontentType ,还需要在dataType传递id参数;

Try the following: 请尝试以下操作:

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

Update 更新资料

Ok, breaking this down into a simpler working example the following will work. 好的,将其分解为一个更简单的工作示例,以下将起作用。

javascript 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>

Have your controller just return the hardcoded values as follows: 让您的控制器仅按以下方式返回硬编码值:

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

Run the above and test that it prints out the values to the console. 运行上面的命令,并测试将其输出到控制台。

Hi at first it looks you didn't call the method. 首先,您好,您似乎没有调用该方法。 I cannot find any call of 'showAllComments' function. 我找不到对“ showAllComments”函数的任何调用。 When I copied your code and just call after declaration 当我复制您的代码并在声明后才调用

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

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

        showAllComments();
    })
</script>

I saw that ApiController method is called, html is updated for me. 我看到调用了ApiController方法,为我更新了html。 Are you sure that you call the 'showAllComments' function? 确定要调用“ showAllComments”功能吗?

And JotaBe's answer is very good, should work. 而且JotaBe的答案很好,应该可以。 And follow for a setTimeout function as JotaBe wrote. 并遵循JotaBe所写的setTimeout函数。 See the description of functions. 请参见功能说明。

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

Have you looked at the routing on the calls and what the real server response is? 您是否查看了呼叫的路由以及真正的服务器响应是什么?

You might want to setup the Route attribute onto the actions then look at quickly calling it using Postman to see the responses. 您可能希望将Route属性设置到操作上,然后看一下使用Postman快速调用它以查看响应。 I found without using postman it made checking the routing a lot harder than it needed to be. 我发现没有使用邮递员,这会使检查路由变得比所需困难得多。

The example items seem to just call the root as /api/comments/1 or api/comments/?id=1 where the get call looks to be /api/comments/get?id=1 from the routing as IIRC default routing would have the action as Index not get or view. 示例项似乎只是将根称为/ api / comments / 1或api / comments /?id = 1,其中,从路由中获取的调用看起来是/ api / comments / get?id = 1,这与IIRC默认路由一样具有索引未获取或查看的操作。

I'll look at this in a bit more detail later when I can spin up a demo project and test out what is going on 稍后,我可以启动演示项目并测试正在发生的事情时,我将对其进行详细介绍。

edit: if you add the attributes to the controller/action you can see the way the calls are constructed a little easier: 编辑:如果您将属性添加到控制器/操作,您可以看到构造调用的方式更简单一些:

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

In this example you would call a get to /api/account/soumeroute?id=123123 and you would get 1 back. 在此示例中,您将调用get到/ api / account / soumeroute?id = 123123并获得1的返还。 The HTTPGet is just a help for separating code on readability (and the XML API documentation if you have it enabled, it's a nice to have) HTTPGet只是一个帮助分离代码可读性的帮助(如果启用了XMLAPI文档,那么很高兴)

tDid you try to add this: t您是否尝试添加以下内容:

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

to your $.ajax call? 您的$ .ajax电话?

Or try this way: 或尝试这种方式:

$.ajax({
                url: "/api/CommentApi/" + "@Model[i].PostId.ToString()",

                dataType: 'json',
                async: true,
                type: 'GET',
                error: function () {
                    alert('Error');
                }

            success: function (data) {
                alert('Success')
            }});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM