带有 ASP.NET MVC 的 jquery - 调用启用 ajax 的 Web 服务

Posted

技术标签:

【中文标题】带有 ASP.NET MVC 的 jquery - 调用启用 ajax 的 Web 服务【英文标题】:jquery with ASP.NET MVC - calling ajax enabled web service 【发布时间】:2011-02-19 15:03:09 【问题描述】:

这是previous问题的延续。

现在我正在尝试调用我在 ASP.NET MVC 应用程序中定义的启用 AJAX 的 Web 服务(即MovieService.svc)。但是我的getMovies javascript 函数中从未调用过该服务。

如果我在非 ASP.NET MVC 应用程序中尝试这种调用 AJAX Web 服务的相同技术,它可以正常工作,所以我想知道当它尝试创建 AJAX 时,ASP MVC 路由是否会以某种方式干扰事物网络服务调用。

你知道为什么我的网络服务没有被调用吗?代码如下。

    <script src="<%= ResolveClientUrl("~/scripts/jquery-1.4.2.min.js") %>" type="text/javascript"></script>

    <script src="<%= ResolveClientUrl("~/scripts/grid.locale-en.js") %>" type="text/javascript"></script>

    <script src="<%= ResolveClientUrl("~/scripts/jquery-ui-1.8.1.custom.min.js") %>"
        type="text/javascript"></script>

    <script src="<%= ResolveClientUrl("~/scripts/jquery.jqGrid.min.js") %>" type="text/javascript"></script>

    <script type="text/javascript">
        var lastsel2;

        function successFunction(jsondata) 
            debugger
            var thegrid = jQuery("#editgrid");
            for (var i = 0; i < jsondata.d.length; i++) 
                thegrid.addRowData(i + 1, jsondata.d[i]);
            
        

        function getMovies() 
            debugger
            // ***** the MovieService#GetMovies method never gets called
            $.ajax(
                url: 'MovieService.svc/GetMovies',
                data: "",  // For empty input data use "",
                dataType: "json",
                type: "GET",
                contentType: "application/json; charset=utf-8",
                success: successFunction
            );
        

        jQuery(document).ready(function() 
            jQuery("#editgrid").jqGrid(
                datatype: getMovies,
                colNames: ['id', 'Movie Name', 'Directed By', 'Release Date', 'IMDB Rating', 'Plot', 'ImageURL'],
                colModel: [
                   name: 'id', index: 'Id', width: 55, sortable: false, hidden: true, editable: false, editoptions:  readonly: true, size: 10 ,
                   name: 'Movie Name', index: 'Name', width: 250, editable: true, editoptions:  size: 10 ,
                   name: 'Directed By', index: 'Director', width: 250, align: 'right', editable: true, editoptions:  size: 10 ,
                   name: 'Release Date', index: 'ReleaseDate', width: 100, align: 'right', editable: true, editoptions:  size: 10 ,
                   name: 'IMDB Rating', index: 'IMDBUserRating', width: 100, align: 'right', editable: true, editoptions:  size: 10 ,
                   name: 'Plot', index: 'Plot', width: 150, hidden: false, editable: true, editoptions:  size: 30 ,
                   name: 'ImageURL', index: 'ImageURL', width: 55, hidden: true, editable: false, editoptions:  readonly: true, size: 10 
                ],
                pager: jQuery('#pager'),
                rowNum: 5,
                rowList: [5, 10, 20],
                sortname: 'id',
                sortorder: "desc",
                height: '100%',
                width: '100%',
                viewrecords: true,
                imgpath: '/Content/jqGridCss/redmond/images',
                caption: 'Movies from 2008',
                editurl: '/Home/EditMovieData/',
                caption: 'Movie List'
            );

            $("#bedata").click(function() 
                var gr = jQuery("#editgrid").jqGrid('getGridParam', 'selrow');
                if (gr != null)
                    jQuery("#editgrid").jqGrid('editGridRow', gr,  height: 280, reloadAfterSubmit: false );
                else
                    alert("Hey dork, please select a row");
            );            

        );

    </script>

    <h2>
        <%= html.Encode(ViewData["Message"]) %></h2>
    <p>
        To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">
            http://asp.net/mvc</a>.
    </p>
    <table id="editgrid">
    </table>
    <div id="pager" style="text-align: center;">
    </div>
    <input type="button" id="bedata" value="Edit Selected" />

这是我的 RegisterRoutes 代码:

public static void RegisterRoutes(RouteCollection routes)

    routes.IgnoreRoute("resource.axd/*pathInfo");
    routes.IgnoreRoute("*MovieService.svc*");

    routes.MapRoute(
        "Default",                                              // Route name
        "controller/action/id",                           // URL with parameters
        new  controller = "Home", action = "Index", id = ""   // Parameter defaults
    );

我的 MovieService 类如下所示:

namespace jQueryMVC

    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class MovieService
    
        // Add [WebGet] attribute to use HTTP GET
        [OperationContract]
        [WebGet(ResponseFormat = WebMessageFormat.Json)]
        public IList<Movie> GetMovies()
        
            return Persistence.GetMovies();
        

    

【问题讨论】:

你能显示你的路线吗?您可能需要一个 IgnoreRoute 用于 MovieService.svc 我已编辑我的回复以显示路线。我为 MovieService 添加了 IgnoreRoute,但它没有改变任何东西,仍然没有调用 Web 服务。 您能否在 MovieService.svc 中发布 GetMovies 函数的原型(接口)。您的代码中的问题很清楚。我会重写一点你的代码,让它更容易(并解决你的主要问题)。 有趣。刚刚用一个新的测试项目尝试了这个,可以确认行为。似乎不是路由问题,因为您可以直接浏览到 Web 服务。当我有更多时间时,我将不得不玩一下,看看我是否能解决这个问题。 我希望我的代码能在你的环境中运行。我对路由没有任何问题,但routes.IgnoreRoute("*MovieService.svc*") 似乎是个好主意。如果某些东西不适用于 MVC/WFC,我们可以比较您和我的项目中的更多部分。再说一句:你怎么能看到我的英文不好,所以如果你发现一些明显的错误,请编辑我的答案并修复这些。 【参考方案1】:

您的主要问题是您在 ajax 调用中使用的不是绝对 URL。 web.config 中的错误条目也会造成问题。此外,您使用datatype: getMovies 而不是datatype: 'json'postData: yourData。以datatype 为函数的方式存在(参见http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#function),但是从jqGrid 3.6.5 开始,您可以在jsonReader 中更直接地读取从Web 服务器返回的数据。

更新: 在我看来,我将在稍后描述编辑功能,并在这里解释如何获取 JSON 数据并在 jqGrid 中填充。

首先 jqGrid 可以从服务器请求自己的 JSON 数据。所以我们不需要单独调用jQuery.ajax。您只需要定义一个指向服务器的 URL 并定义一些您喜欢的附加 jQuery.ajax 参数。 您没有在问题中发布 Movie 类的定义。所以我自己定义如下

public class Movie 
    public int Id  get; set; 
    public string Name  get; set; 
    public string Director  get; set; 
    public string ReleaseDate  get; set; 
    public string IMDBUserRating  get; set; 
    public string Plot  get; set; 
    public string ImageURL  get; set; 

您应该注意,Microsoft 序列化 DataTime 类型不是作为可读的日期字符串,而是作为字符串 /Date(utcDate)/,其中 utcDate 是这个数字 (见jQuery.param() - doesn't serialize javascript Date objects?)。为了在开始时减少问题,我将ReleaseDate 定义为字符串。

方法IList&lt;Movie&gt; GetMovies() 像对象数组Movie 一样返回JSON 数据。所以 jqGrid 作为对 HTTP GET 请求的响应,从 MovieService.svc/GetMovies URL 接收如下数据:

 ["Id":1, "Name": "E.T.", "Director": "Steven Spielberg",...,...,...]

我可以说它不是典型的数据格式,等待jqGrid(与http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data比较)。为了能够将数据放在 jqGrid 中,我们必须定义一个 jsonReader。所以我们做以下

jQuery("#editgrid").jqGrid(
    url: '<%= Url.Content("~/MovieService.svc/GetMovies")%>',
    datatype: 'json',
    ajaxGridOptions:  contentType: "application/json" ,
    jsonReader:  repeatitems: false, id: "Id", root: function(obj)  return obj; ,
    headertitles: true,
    sortable: true,
    colNames: ['Movie Name', 'Directed By', 'Release Date',
               'IMDB Rating', 'Plot', 'ImageURL'],
    colModel: [
         name: 'Name', width: 250,
         name: 'Director', width: 250, align: 'right' ,
         name: 'ReleaseDate', width: 100, align: 'right' ,
         name: 'IMDBUserRating', width: 100, align: 'right' ,
         name: 'Plot', width: 150 ,
         name: 'ImageURL', width: 55, hidden: true 
    ],
    pager: jQuery('#pager'),
    pginput: false,
    rowNum: 0,
    height: '100%',
    viewrecords: true,
    rownumbers: true,
    caption: 'Movies from 2008'
).jqGrid('navGrid', '#pager',  add: false, edit: false, del: false, search: false );

备注:我从示例中删除了任何排序参数,因为在请求 JSON 数据的情况下,排序参数将仅发送到服务器(一些附加参数附加服务器 URL)和服务器必须返回已排序的数据。有关详细信息,请参阅http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options 上的prmNames 参数说明和http://www.trirand.com/jqgridwiki/doku.php?id=wiki:singe_searching 上的sopt 参数说明。

关于datatype: 'json',我们定义jQuery.ajaxdataType: 'json'参数(不要混淆datatype参数内部的大小写)。 colModel 内所有字段的名称我们定义 exact 与 JSON 对象内的字段名称相同。一些附加参数viewrecordsrownumberssortableheadertitles 在此示例中不是很重要,我选择那里是因为 1)我喜欢那里 2)我设置了 rowNum: 0 以使选项 @987654367 成为可能@ 工作正常,如果 rowNum: 5 像您的原始示例一样,不会向我们显示以 -5 开头的负行号。

使用ajaxGridOptions: contentType: "application/json" ,我们定义了额外的参数,这些参数将直接转发到jQuery.ajax

这个例子最复杂的部分是

jsonReader:  repeatitems: false, id: "Id", root: function(obj)  return obj; 

它定义所有行的 id 都具有名称“Id”(参见class Movie 的定义)。 “repeatitems: false”表示我们希望通过字段名称(在colModel 中定义)而不是每个位置的默认定义来识别每个数据字段。 root 的定义有点奇怪,但它定义了如何在 JSON 数据中找到 rowsroot。 JSON数据的默认格式如下


  total: "xxx", 
  page: "yyy", 
  records: "zzz",
  rows : [
    id:"1", cell:["cell11", "cell12", "cell13"],
    id:"2", cell:["cell21", "cell22", "cell23"],
      ...
  ]

并且行的根定义为root: "rows"。所以如果将JSON数据赋值给变量res,则根可以返回为res.rows。为了让 jqGrid 读取我们的数据,我们将 jsonReader.root 定义为一个函数(此功能自 jqGrid 3.6.5 起就存在,请参阅 http://www.trirand.com/jqgridwiki/doku.php?id=wiki:change#additions_and_changes)。您可以验证这种奇怪的方法是否有效。典型的附加参数pagetotallastpage)和records在我们的JSON数据中不存在,它们将被初始化为page:0, total:1, records:0。所以我们无法进行数据分页。您可以使用定义pagetotalrecords(也作为函数)的函数来扩展jsonReader,例如

jsonReader: 
    repeatitems: false,
    id: "Id",
    root: function (obj)  return obj; ,
    page: function (obj)  return 1; ,
    total: function (obj)  return 1; ,
    records: function (obj)  return obj.length; 

这将完成我们的 jsonReader。则不再需要设置rowNum: 0

我展示这种方式只是为了展示 jqGrid 的灵活性。仅当您访问无法更改的 Web 服务器时,才应使用所述方式。 jqGrid具有分页排序和两种搜索(更多是在相应的SELECT中使用WHERE过滤)数据:简单和高级.如果我们想在我们的网页上的 jqGrid 中拥有这些不错的功能,我们应该在 Web Service 中定义一个额外的方法,例如

[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json,
        UriTemplate = "jqGridGetTestbereiche?_search=_search&page=page&"+
                      "rows=rows&sidx=sortIndex&sord=sortDirection&"+
                      "searchField=searchField&searchString=searchString&"+
                      "searchOper=searchOper&filters=filters")]
public jqGridTable jqGridGetMovies(
  int page, int rows, string sortIndex, string sortDirection,
  string _search, string searchField, string searchString,
  string searchOper, string filters)

在哪里jqGridTable

public class jqGridTable

    public int total  get; set;       // total number of pages
    public int page  get; set;        // current zero based page number
    public int records  get; set;     // total number of records
    public List<jqGridRow> rows  get; set; 

public class jqGridRow

    public string id  get; set; 
    public List<string> cell  get; set; 

或者如果我们想使用从服务器传输到客户端的最紧凑的数据形式,那么

// jsonReader:  repeatitems : true, cell:"", id: "0" 
public class jqGridTable 
    public int total  get; set;           // total number of pages
    public int page  get; set;            // current zero based page number
    public int records  get; set;         // total number of records
    public List<List<string>> rows  get; set; // first element in every row must be id of row.

(如果您在左侧树部分选择“数据映射”然后选择“数据优化”,您可以在http://www.trirand.com/blog/jqgrid/jqgrid.html 上阅读有关这种数据传输的更多信息)

P.S.:关于 jsonReader,您可以在 http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data 上阅读更多信息。我的一个旧答案Mapping JSON data in JQGrid 对您来说也可能很有趣。

更新 2:因为您没有将答案标记为已接受,所以您会遇到一些问题。因此,我在 Visual Studio 2010 中创建了一个新项目来演示我编写的内容。您可以从http://www.ok-soft-gmbh.com/jqGrid/jQueryMVC.zip 下载源代码。对比一下你的项目,尤其是jqGrid的参数为完整url的部分和描述WCF服务接口的web.config部分。

UPDATED 3:我用VS2010的时间不长。所以我可以很快把它降级到VS2008。所以几乎相同的代码在 Visual Studio 2008 中工作,但使用 ASP.NET MVC 2.0,您可以从 http://www.ok-soft-gmbh.com/jqGrid/VS2008jQueryMVC.zip 下载。 ASP.NET MVC 1.0 中的代码应该是相同的,但是应该修补项目文件中的 GUID 和 Web.config 中的一些字符串(请参阅http://www.asp.net/learn/whitepapers/aspnet-mvc2-upgrade-notes)。

【讨论】:

@Oleg - 是的,我是 jqGrid 的新手,但我正在努力学习它,但它很难使用,而且示例很差。虽然我不太明白你的回答。我已经使用 datatype: 'json' 让控制流向控制器。我试图让它与对 Web 服务的调用一起工作。这在我的非 MVC 项目中有效,但在 MVC 中无效,这似乎表明 MVC 在某处搞砸了。 @Oleg - 当我说这些例子很糟糕时,我应该说明我的意思。 JQGrid 的例子本身就很好,但是都是用php 写的,asp.net 里没有。我想这是因为他们希望你购买商业控制 :)。 我在一个用 ASP.NET MVC 编写的项目中使用 jqGrid,该项目带有一个 RESTfull WCF 服务,该服务是同一个 ASP.NET 站点的一部分。所以我们使用相同的技术。我和我女儿玩了一会儿,给你举个例子 @Oleg - 感谢您的大力帮助。我很快就会尝试你的解决方案,我只是还没有机会,因为我一直忙于其他一些项目。我会让你知道我发现了什么。再次感谢您的详细解释。 @Oleg - 这是 VS 2010 解决方案吗?我只有 VS 2008,所以我无法打开解决方案文件:(。无论如何,你已经花了足够的时间,我会继续接受答案,这样你就可以得到你的代表点。【参考方案2】:

这是因为 global.asax 中注册的路由不会识别这个 .svc 文件。它将尝试使用操作 getmovies 搜索该控制器并且将失败。尝试使用 firebug 进行调试。 您可以通过忽略 global.asax 中的这条路线来解决此问题

【讨论】:

没什么区别。我添加了 routes.IgnoreRoute("MovieService.svc");到 RegisterRoutes,还尝试了 routes.IgnoreRoute("MovieService.svc/GetMovies")。都没有用。 你试过从浏览器访问服务吗? 据我所知,它在浏览器中可以正常工作。当然,我们不能从浏览器调用 WCF 网络方法(不像 asmx 网络方法)。【参考方案3】:

我遇到了同样的问题。我得出的结论是路由干扰了服务调用。你试过 Phil Haack 的Route Debugger 吗?它救了我几次培根。

最后,我在其中一个控制器上创建了一个端点。

【讨论】:

是的,我刚刚尝试了路由调试器并且路由匹配。正如我在回复 Oleg 时所说的那样,我认为 MVC 只是在尝试调用 Web 服务时把事情搞砸了。很高兴知道其他人也有同样的问题,而且我并不孤单:)。【参考方案4】:

奥列格,

当我正在使用 jqgrid/asp.net mvc 和一个安静的服务并且有一个时间问题时,您是否有您正在谈论的示例。当我在墙上时,这将有助于看到一个例子。谢谢

扫描电镜

【讨论】:

以上是关于带有 ASP.NET MVC 的 jquery - 调用启用 ajax 的 Web 服务的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET MVC - 使用带有匿名类型和 Jquery 的 JavaScriptStringEncode()

带有 jquery 代码的脚本块应该放在 ASP.NET MVC 母版页的啥位置?

带有 ASP.NET MVC 的 jquery - 调用启用 ajax 的 Web 服务

如何在带有 ADO.NET 的 ASP.NET Core MVC 中使用 jQuery Ajax 自动完成

在 asp.net mvc 4 中使用带有部分视图的 jQuery

带有 jquery 验证和 Html.EditorFor 以及不同小数分隔符的 asp.net mvc