jquery с ASP.NET MVC - вызов веб-службы с поддержкой ajax

Это своего рода продолжение предыдущего вопроса.

Теперь я пытаюсь вызвать веб-службу с поддержкой AJAX, которую я определил в приложении ASP.NET MVC (т.е. MovieService.svc). Но служба никогда не вызывается в моей getMovies функции javascript.

Этот же метод вызова веб-службы AJAX работает нормально, если я попробую его в приложении, отличном от ASP.NET MVC, поэтому я задаюсь вопросом, могут ли маршруты ASP MVC каким-то образом мешать чему-то, когда он пытается сделать вызов веб-службы AJAX .

Вы хоть понимаете, почему мне не звонят в мою веб-службу? Код ниже.

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

    }
}

person dcp    schedule 14.05.2010    source источник
comment
Можете показать свои маршруты? Вам, вероятно, понадобится IgnoreRoute для MovieService.svc   -  person Joel    schedule 14.05.2010
comment
Я отредактировал свой ответ, чтобы показать маршруты. Я добавил IgnoreRoute для MovieService, но он ничего не изменил, веб-сервис по-прежнему не вызывается.   -  person dcp    schedule 14.05.2010
comment
Не могли бы вы опубликовать прототип (интерфейс) функции GetMovies внутри MovieService.svc. Проблема в вашем коде очевидна. Я бы немного переписал ваш код и упростил его (и исправил вашу основную проблему).   -  person Oleg    schedule 14.05.2010
comment
Интересный. Просто попробовал это с новым тестовым проектом и могу подтвердить поведение. Кажется, это не проблема маршрутизации, так как вы можете напрямую перейти к веб-службе. Мне придется немного поиграть, когда у меня будет больше времени, и посмотрю, смогу ли я это понять.   -  person Bradley Mountford    schedule 14.05.2010
comment
Надеюсь, мой код будет работать в вашей среде. У меня не было проблем с маршрутизацией, но routes.IgnoreRoute("*MovieService.svc*") кажется мне хорошей идеей. Если что-то не работает с MVC / WFC, мы можем сравнить другие части из вашего и моего проектов. Еще одно замечание: как вы могли видеть, что мой английский не очень хорош, поэтому, если вы обнаружите какие-то явные ошибки, отредактируйте мой ответ и просто исправьте их.   -  person Oleg    schedule 15.05.2010
comment
dcp, если что-то сделаю, то постараюсь довести до конца. Я хочу, чтобы вы решили вашу проблему. Итак, вы используете VS2008. Какую версию ASP.MVC вы используете 2.0 или 1.0?   -  person Oleg    schedule 16.05.2010
comment
@Oleg - ценю вашу самоотдачу :). Я использую версию 1.0 ASP.MVC.   -  person dcp    schedule 16.05.2010
comment
У вас есть хорошая возможность сделать обновление до 2.0! Я вам больше помогаю, потому что у вас высокая репутация. Для меня это означает, что вы раньше помогали многим людям. Итак, вы заслуживаете, чтобы кто-нибудь помог вам, если у вас возникла проблема. Около 20% людей, которым я написал ответ с реальным решением его проблемы, мне ничего не отвечают. Я уверен, вы знаете эту проблему. Но такова жизнь. Я трачу большую часть своего времени на то, чтобы зарабатывать деньги и на свою семью, но я считаю также важным помогать другим людям (кстати, я не религиозен), и я не жду большего в качестве благодарности. Так что удачи, dcp!   -  person Oleg    schedule 16.05.2010


Ответы (4)


Ваша основная проблема в том, что вы используете не абсолютные URL-адреса в вызове ajax. Неправильные записи в 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 для чтения данных, возвращаемых с веб-сервера.

ОБНОВЛЕНО: мне кажется, что описание функций редактирования я сделаю позже и объясню здесь, как получить данные 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 () - не сериализует ли объекты даты javascript?). Чтобы вначале было меньше проблем, я определяю ReleaseDate как строку.

Метод IList<Movie> GetMovies() возвращает данные JSON в виде массива объектов Movie. Итак, 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-адрес сервера), и сервер должен вернуть отсортированные данные. Для получения дополнительной информации см. Описание параметра prmNames на http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options и описание параметра sopt в http://www.trirand.com/jqgridwiki/doku.php?id=wiki:singe_searching.

Что касается datatype: 'json', мы определяем параметр dataType: 'json' для jQuery.ajax (не путайте регистр внутри параметра datatype). Имена всех полей внутри colModel мы определяем точно так же, как имена полей внутри наших объектов JSON. Некоторые дополнительные параметры viewrecords, rownumbers, sortable и headertitles не очень важны в этом примере, я выбрал их, потому что 1) они мне нравятся и 2) я установил rowNum: 0, чтобы параметры rownumbers: true работали правильно и не отображали отрицательные номера строк, начинающиеся с -5, если rowNum: 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. Формат данных 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#adds_and_changes). Вы можете убедиться, что этот странный метод работает. Типичные дополнительные параметры page, total (lastpage) и records не существуют в наших данных JSON, и они будут инициализированы следующим образом page:0, total:1, records:0. Таким образом, мы не можем выполнять подкачку данных. Вы можете расширить jsonReader функциями, определяющими page, total и records (также как функции), например

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. Вы должны использовать описанный способ только в том случае, если вы получаете доступ к веб-серверу, который вы не можете изменить. jqGrid имеет такие функции, как разбиение по страницам, сортировка и два вида поиска (больше как фильтрация с помощью WHERE в соответствующем SELECT) данных: простой и расширенный . Если мы хотим иметь эти замечательные функции внутри jqGrid на наших веб-страницах, мы должны определить в веб-службе дополнительный метод, например

[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, если вы выберете в левой части дерева «Сопоставление данных», а затем «Оптимизация данных»)

PS: Подробнее о jsonReader вы можете узнать на http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data. Один мой старый ответ Отображение данных JSON в JQGrid также может быть интересным для ты.

ОБНОВЛЕНО 2. Поскольку вы не отметили ответ как принятый, у вас останутся проблемы. Итак, я создал новый проект в Visual Studio 2010, который демонстрирует то, что я написал. Вы можете загрузить исходный код с http://www.ok-soft-gmbh.com/jqGrid/jQueryMVC.zip. Сравните с вашим проектом, особенно с частью с полным URL-адресом в качестве параметра jqGrid и частью web.config, описывающей интерфейс службы WCF.

ОБНОВЛЕНО 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).

person Oleg    schedule 14.05.2010
comment
@Oleg - Да, я новичок в jqGrid, но очень стараюсь изучить его, но его очень сложно использовать, а примеры плохие. Хотя я не совсем понимаю твой ответ. У меня уже была такая работа с использованием типа данных: 'json' и передачей управления контроллеру. Я пытался заставить его работать с вызовом веб-службы. Это работает в моем проекте, отличном от MVC, но не работает в MVC, что, кажется, указывает на то, что MVC где-то все испортил. - person dcp; 14.05.2010
comment
@ Олег - Я должен пояснить, что имел в виду, когда сказал, что примеры плохие. Сами примеры JQGrid очень хороши, но все они написаны на php, а на asp.net их нет. Я думаю, это связано с тем, что они хотят, чтобы вы купили коммерческие элементы управления :). - person dcp; 14.05.2010
comment
Я использую jqGrid в проекте, написанном на ASP.NET MVC со службой RESTfull WCF, которая является частью того же сайта ASP.NET. Итак, мы используем ту же технику. Я немного поиграю с дочкой и отправлю вам пример - person Oleg; 15.05.2010
comment
@Oleg - Спасибо за большую помощь. Я собираюсь опробовать ваше решение очень скоро, просто у меня еще не было шанса, потому что я был занят некоторыми другими проектами. Я дам вам знать, что я нашел. Опять же, я ценю подробное объяснение. - person dcp; 15.05.2010
comment
@Oleg - Это решение VS 2010? У меня только VS 2008, поэтому я не могу открыть файл решения :(. В любом случае, вы потратили на это достаточно времени, я приму ответ, чтобы вы получили свои очки репутации. - person dcp; 16.05.2010
comment
@Oleg - Что касается обновления 3, спасибо, я смог запустить его сразу после того, как установил MVC 2.0. Я думаю, что я получил основное представление об этом. В других примерах, которые я рассмотрел, действительно выполнялось разбиение по страницам, получая параметры внутри метода контроллера, например: string sortColumn = (Request.Params [sidx]) ;. Еще один вопрос: вы когда-нибудь использовали jqGrid в приложении, отличном от ASP.net, с обычными веб-формами? Я не нашел простого способа сделать это, думаю, он больше ориентирован на шаблон MVC. Еще раз спасибо! - person dcp; 17.05.2010

Это связано с тем, что зарегистрированный маршрут в global.asax не распознает этот файл .svc. он попытается найти этот контроллер с помощью действия getmovies и потерпит неудачу. попробуйте отладку с помощью firebug. вы можете исправить это, игнорируя этот маршрут в global.asax

person Sundararajan S    schedule 14.05.2010
comment
Это не имело значения. Я добавил routes.IgnoreRoute (MovieService.svc); в RegisterRoutes, а также пробовал с помощью routes.IgnoreRoute (MovieService.svc / GetMovies). Ни то, ни другое не сработало. - person dcp; 14.05.2010
comment
Вы пробовали получить доступ к сервису из браузера? - person Sundararajan S; 14.05.2010
comment
Насколько я могу судить, в браузере он работает нормально. Конечно, мы не можем вызывать веб-методы WCF из браузера (в отличие от веб-методов asmx). - person dcp; 15.05.2010

Я столкнулся с той же проблемой. Я пришел к выводу, что маршруты мешали звонку в сервис. Вы пробовали Route Debugger Фила Хаака? Пару раз это спасло мой бекон.

В конце концов, я создал конечную точку на одном из контроллеров.

person Chuck Conway    schedule 14.05.2010
comment
Да, я только что попробовал отладчик маршрута, и маршрут совпал. Как я сказал в своем ответе Олегу, я думаю, что MVC просто куда-то заморачивается при попытке вызвать веб-службу. Приятно знать, что у кого-то была такая же проблема, и что я не одинок :). - person dcp; 14.05.2010

Олег,

У вас есть пример, о котором вы говорили, поскольку я работаю с jqgrid / asp.net mvc и успокаивающим сервисом, и у меня есть время. Было бы полезно увидеть пример, как я стою у стены. Спасибо

SEM

person user351978    schedule 27.05.2010