jquery con ASP.NET MVC: llamar al servicio web habilitado para ajax

14 minutos de lectura

jquery con ASPNET MVC llamar al servicio web habilitado para
dcp

Esto es un poco la continuación de una pregunta anterior.

Ahora estoy tratando de hacer una llamada a un servicio web habilitado para AJAX que he definido dentro de la aplicación ASP.NET MVC (es decir, el MovieService.svc). Pero el servicio nunca se llama en mi getMovies función javascript.

Esta misma técnica de llamar al servicio web AJAX funciona bien si lo pruebo en una aplicación que no sea ASP.NET MVC, por lo que me pregunto si tal vez las rutas ASP MVC están interfiriendo con las cosas de alguna manera cuando intenta realizar la llamada al servicio web AJAX. .

¿Tiene alguna idea de por qué no se llama a mi servicio web? Código a continuación.

    <script src="https://stackoverflow.com/questions/2835957/<%=%20ResolveClientUrl("~/scripts/jquery-1.4.2.min.js") %>" type="text/javascript"></script>

    <script src="https://stackoverflow.com/questions/2835957/<%=%20ResolveClientUrl("~/scripts/grid.locale-en.js") %>" type="text/javascript"></script>

    <script src="https://stackoverflow.com/questions/2835957/<%=%20ResolveClientUrl("~/scripts/jquery-ui-1.8.1.custom.min.js") %>"
        type="text/javascript"></script>

    <script src="https://stackoverflow.com/questions/2835957/<%=%20ResolveClientUrl("~/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" />

Aquí está mi código de 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
    );
}

Así es como se ve mi clase 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();
        }

    }
}

  • ¿Puedes mostrar tus rutas? Probablemente necesite un IgnoreRoute para MovieService.svc

    – Joel

    14 mayo, 2010 a las 16:50

  • He editado mi respuesta para mostrar las rutas. Agregué IgnoreRoute para MovieService, pero no cambió nada, el servicio web aún no se llama.

    – dcp

    14 mayo, 2010 a las 18:05

  • ¿Podría publicar un prototipo (interfaz) de la función GetMovies dentro de MovieService.svc? El problema en tu código es claro. Reescribiría un poco su código y lo haría más fácil (y solucionaría su principal problema de causa).

    – Oleg

    14 mayo, 2010 a las 18:37

  • Interesante. Acabo de probar esto con un nuevo proyecto de prueba y puedo confirmar el comportamiento. No parece ser un problema de enrutamiento ya que puede navegar directamente al servicio web. Tendré que jugar un poco cuando tenga más tiempo y ver si puedo resolver esto.

    –Bradley Mountford

    14 mayo, 2010 a las 18:58

  • Espero que mi código funcione en su entorno. No tuve problemas con el enrutamiento, pero routes.IgnoreRoute("*MovieService.svc*") me parece una buena idea. Si algo no funciona con MVC/WFC, podemos comparar más partes de sus proyectos y los míos. Una observación más: cómo puede ver que mi inglés no es bueno, por lo que si encuentra algunos errores claros, edite mi respuesta y simplemente corríjalos.

    – Oleg

    15 mayo 2010 en 0:42

1644217991 750 jquery con ASPNET MVC llamar al servicio web habilitado para
Oleg

Su principal problema es que no utiliza URL absolutas en el ajax llamar. Entradas incorrectas en web.config puede hacer también problemas. Además usas datatype: getMovies en vez de datatype: 'json' y postData: yourData. el camino con datatype como funciones existen (ver http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#function), pero desde jqGrid 3.6.5 tienes una forma más directa dentro de jsonReader para leer los datos devueltos por el servidor web.

ACTUALIZADO:
Me parece que describiré las funciones de edición que haré más adelante y explicaré aquí cómo obtener datos JSON y llenarlos dentro de jqGrid.

En primer lugar, jqGrid puede solicitar los datos JSON del servidor. Así que no necesitamos hacer un separado jQuery.ajax llamar. Solo necesita definir una URL que apunte al servidor y definir algunos adicionales jQuery.ajax parámetros que prefiera. No publicas en tu pregunta la definición de la Movie clase. Así que lo defino yo mismo como siguiente

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

Deberías comentar que Microsoft serializa DataTime escriba no como una cadena de fecha legible sino como una cadena /Date(utcDate)/, donde utcDate es este número (ver jQuery.param() – ¿no serializa objetos de fecha de javascript?). Para hacer menos problemas al principio defino ReleaseDate como cuerda.

Método IList<Movie> GetMovies() devuelve datos JSON como una matriz de objetos Movie. Entonces jqGrid como respuesta a HTTP GET solicitar recibir de la MovieService.svc/GetMovies Dirija los datos de la siguiente manera:

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

Puedo decir que no es el formato típico de datos, que están esperando jqGrid (comparar con http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data). Para poder colocar los datos dentro de jqGrid debemos definir un jsonReader. Entonces hacemos lo siguiente

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

OBSERVACIÓN: elimino del ejemplo cualquier parámetro de clasificación, porque en caso de solicitud de datos JSON, el parámetro de clasificación solo se enviará al servidor (algunos parámetros adicionales agregan la URL del servidor) y el servidor debe devolver los datos ordenados. Para más información ver descripción de prmNames parámetro en http://www.trirand.com/jqgridwiki/doku.php?id=wiki:opciones y descripción de sopt parámetro en http://www.trirand.com/jqgridwiki/doku.php?id=wiki:singe_searching.

Con respeto de datatype: 'json' definimos dataType: 'json' parámetro de jQuery.ajax (no confunda el caso dentro de datatype parámetro). Los nombres de todos los campos dentro de colModel definimos exacto lo mismo que los nombres de campo dentro de nuestros objetos JSON. Algunos parámetros adicionales viewrecords, rownumbers, sortable y headertitles no son muy importantes en este ejemplo, elegí allí porque 1) me gusta allí y 2) establecí rowNum: 0 para hacer posibles las opciones rownumbers: true funciona correctamente y no nos muestra los números de fila negativos que comienzan con -5 si rowNum: 5 como en tu ejemplo original.

Con ajaxGridOptions: { contentType: "application/json" } definimos parámetros adicionales que serán directo reenviado a la jQuery.ajax.

La parte más compleja de este ejemplo es

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

Define que el id de todas las filas tenga el nombre “Id” (ver definición del class Movie). “repeatitems: false“digamos que cada campo de datos que queremos identificar por el nombre del campo (definido en colModel) en lugar de la definición predeterminada por posición. La definición de root es un poco extraño, pero define cómo encontrar el raíz de filas dentro de los datos JSON. El formato predeterminado de los datos JSON es el siguiente

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

y la raíz de las filas se definen como root: "rows". Entonces, si los datos JSON asignados a la variable res, la raíz se puede devolver como res.rows. Para permitir que jqGrid lea nuestros datos, definimos jsonReader.root como una función (esta característica existe desde jqGrid 3.6.5 ver http://www.trirand.com/jqgridwiki/doku.php?id=wiki:cambio#adiciones_y_cambios). Puedes comprobar que este extraño método funciona. Los parámetros adicionales típicos page, total (lastpage) y records no existen dentro de nuestros datos JSON y se inicializarán de la siguiente manera page:0, total:1, records:0. Así que no podemos hacer paginación de datos. Puedes ampliar jsonReader con funciones que definen page, total y records (también como funciones) como

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

que completará nuestro jsonReader. Entonces ajuste de rowNum: 0 no será más necesario.

Mostré esta forma solo para mostrar la flexibilidad de jqGrid. Debe usar la forma descrita solo si accede a un servidor web que no puede cambiar. jqGrid tiene características como paginación, clasificación y dos tipos de buscando (más como filtrar con DONDE en el SELECT correspondiente) de datos: simple y avanzado. Si queremos tener estas características agradables dentro de jqGrid en nuestras páginas web, debemos definir en el servicio web un método adicional como

[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)

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

O si queremos usar la forma más compacta de datos transferidos del servidor al cliente, entonces

// 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.
}

(puede leer más sobre este tipo de transferencia de datos en http://www.trirand.com/blog/jqgrid/jqgrid.html si elige en la parte izquierda del árbol “Mapeo de datos” y luego “Optimización de datos”)

PD: Acerca de jsonReader puedes leer más en http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data. Una de mis respuestas anteriores Mapear datos JSON en JQGrid también puede ser interesante para usted.

ACTUALIZADO 2: Debido a que no marcas la respuesta como aceptada, te quedas con algunos problemas. Así que creé un nuevo Proyecto en Visual Studio 2010 que demuestra lo que escribí. Puedes descargar la fuente de http://www.ok-soft-gmbh.com/jqGrid/jQueryMVC.zip. Compare con su proyecto, especialmente la parte con la URL completa como parámetro de jqGrid y una parte de web.config que describe la interfaz de servicio de WCF.

ACTUALIZADO 3: Uso VS2010 no hace mucho tiempo. Entonces podría degradar esto rápidamente a VS2008. Entonces, casi el mismo código funciona en Visual Studio 2008, pero con ASP.NET MVC 2.0 puede descargarlo desde http://www.ok-soft-gmbh.com/jqGrid/VS2008jQueryMVC.zip. El código en ASP.NET MVC 1.0 debe ser el mismo, pero se debe parchear un GUID del archivo del proyecto y algunas cadenas de Web.config (consulte http://www.asp.net/learn/whitepapers/aspnet-mvc2-upgrade-notes).

  • @Oleg: sí, soy nuevo en jqGrid pero estoy tratando de aprenderlo, pero es muy difícil de usar y los ejemplos son deficientes. Aunque no entiendo muy bien tu respuesta. Ya tenía esto funcionando usando el tipo de datos: ‘json’ y dejando que el control fluya al controlador. Estaba tratando de hacerlo funcionar con una llamada al servicio web. Eso funciona en mi proyecto que no es MVC, pero no funciona en MVC, lo que parece indicar que MVC está arruinando las cosas en alguna parte.

    – dcp

    14 mayo 2010 en 19:07

  • @Oleg: debería calificar lo que quise decir cuando dije que los ejemplos son deficientes. Los ejemplos de JQGrid en sí son muy buenos, pero todos están en php, no tienen ninguno en asp.net. Supongo que esto se debe al hecho de que quieren que compres los controles comerciales :).

    – dcp

    14 mayo, 2010 a las 19:16

  • Uso jqGrid en un proyecto escrito en ASP.NET MVC con un servicio RESTfull WCF que forma parte del mismo sitio ASP.NET. Así que usamos la misma técnica. Juego un poco con mi hija y te publico un ejemplo

    – Oleg

    14 mayo 2010, 20:09

  • @Oleg – Gracias por la gran ayuda. Voy a probar su solución muy pronto, solo que aún no he tenido la oportunidad porque he estado ocupado con otros proyectos. Te haré saber lo que encuentre. Una vez más, agradezco la explicación detallada.

    – dcp

    15 mayo 2010 a las 12:30

  • @Oleg – ¿Es esta una solución VS 2010? Solo tengo VS 2008, por lo que no puedo abrir el archivo de la solución :(. De todos modos, ha dedicado suficiente tiempo a esto, continuaré y aceptaré la respuesta para que obtenga sus puntos de representante.

    – dcp

    15 mayo 2010 a las 22:38

Esto se debe a que la ruta registrada en global.asax no reconocerá este archivo .svc. intentará buscar ese controlador con la acción getmovies y fallará. prueba la depuración usando firebug. puede solucionar esto ignorando esta ruta en global.asax

  • No hizo ninguna diferencia. Agregué rutas.IgnoreRoute(“MovieService.svc“); a RegisterRoutes, y también lo probé con route.IgnoreRoute(“MovieService.svc/GetMovies”). Tampoco funcionó.

    – dcp

    14 mayo 2010 en 18:03

  • ¿Intentaste acceder al servicio desde el navegador?

    – SundararajanS

    14 mayo, 2010 a las 18:31

  • Por lo que puedo decir, funciona bien desde el navegador. Por supuesto, no podemos invocar métodos web WCF desde el navegador (a diferencia de los métodos web asmx).

    – dcp

    14 mayo 2010, 20:28

Encontré el mismo problema. Llegué a la conclusión de que las rutas estaban interfiriendo con la llamada de servicio. ¿Has probado el de Phil Haack? Depurador de rutas? Me ha salvado el tocino un par de veces.

Al final, creé un punto final fuera de uno de los controladores.

  • Sí, acabo de probar el depurador de rutas y la ruta coincidió. Como dije en mi respuesta a Oleg, creo que MVC simplemente enchufa las cosas en alguna parte cuando intenta llamar al servicio web. Es bueno saber que alguien más tuvo el mismo problema y que no estoy solo :).

    – dcp

    14 mayo 2010 en 19:08

Oleg,

¿Tiene el ejemplo del que estaba hablando, ya que estoy trabajando con jqgrid/asp.net mvc y un servicio tranquilo y pasando un mal rato? Sería útil ver un ejemplo ya que estoy en una pared. Gracias

SEM

.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad