2012-08-30 33 views
7

Por lo tanto, he estado golpeando mi cabeza contra la pared con esto, y no puedo encontrar ninguna buena fuente para esto. Tal vez me estoy olvidando de cómo funciona el encuadernado modelo en MVC3, pero esto es lo que estoy tratando de hacer: Tengo un editor vinculado con Knockout para manejar la edición de un modelo. No hay mucho al modelo:Publicación de un modelo JSON en ASP.Net MVC3 con token Anti-falsificación

public class SetupTemplate 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string Template { get; set; } 
} 

La firma de la acción que estoy tratando de llamar es:

[HttpPost] 
[ValidateAntiForgeryToken] 
public ActionResult UpdateTemplate(SetupTemplate template) 

De otra pregunta aquí, cogí este fragmento más útiles para obtener el símbolo anti-falsificación:

window.addAntiForgeryToken = function(data) { 
    data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val(); 
    return data; 
}; 

cual todo viene junto conmigo tratando de publicar una actualización a través de AJAX:

payload = window.addAntiForgeryToken(ko.mapping.toJS(self.data)); 
$.ajax({ 
    type: "post", 
    url: endpoint, 
    data: payload, 
    success: function(data) { 
     //Handle success 
    }}); 

que se traduce en esto en la sección del formulario de datos de las herramientas para desarrolladores de Chrome

Id:1 
Name:Greeting 
Template: [Template Text] 
__RequestVerificationToken: [The really long anti-forgery token] 

El token antiforgery se recogió, pero mi modelo es nula. La mayoría de los ejemplos que he visto de esto solo usan un único parámetro pasado, y no un modelo.

Estoy seguro de que me falta algo obvio, ¿alguna idea de lo que podría ser?

EDIT: En respuesta a @ Marcos, cambiando la llamada a este:

$.ajax({ 
type: "post", 
dataType: "json", 
contentType: 'application/json', 
url: endpoint, 
data: JSON.stringify(payload), 
success: function(data) { 
    //Do some stuff 
}}); 

resultados en una carga útil de esta solicitud:

{"Id":1,"Name":"Greeting","Template":"...","__RequestVerificationToken":"..."}: 

y el servidor no recoger el anti token de falsificación Esto se intentó con y sin los parámetros contentType a $.ajax().

Respuesta

1

@ Marcos se lleva el crédito por guiarme por el camino correcto para esto, y me señalando a algunos enlaces que ahora que me encargue de la lucha contra la falsificación símbolo en lugar de forma transparente. Sin embargo, lo que resuelve el problema estaba cambiando:

public ActionResult UpdateTemplate(SetupTemplate template) 

a:

public ActionResult UpdateTemplate(SetupTemplate model) 

Y ahora está correctamente rellenar los valores. Realmente me gustaría saber por qué lo solucionó, pero por ahora, funciona.

1

¿Puedes intentar usar JSON.stringify?

$.ajax({  
    type: "post",  
    url: endpoint,  
    data: JSON.stringify(payload),  
    success: function(data) {   
     //Handle success  
    } 
}); 
+0

Actualicé mi pregunta con una respuesta a esto. En resumen: todavía no hay dados. –

+1

¿Te ayuda esta publicación http://haacked.com/archive/2011/10/10/preventing-csrf-with-ajax.aspx? – VJAI

+0

y este http://stackoverflow.com/questions/2906754/how-can-i-supply-an-antiforgerytoken-when-posting-json-data-using-ajax – VJAI

2

La asignación no funcionaba con el parámetro como plantilla porque choca con una de las propiedades que tienen el mismo nombre (estuche). Si usa cualquier otra cosa que no sea la plantilla, funcionará bien para ese parámetro de controlador.

Hay un enlace que explica los detalles, no puedo encontrarlo ahora fácilmente.

public class SetupTemplate 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string Template { get; set; } 
} 
2

Aquí está mi solución.Defina una función jQuery como esta:

(function ($) { 
    $.getAntiForgeryToken = function() { 
     return $('input[name="__RequestVerificationToken"]').val(); 
    }; 

    // (!) use ValidateJsonAntiForgeryToken attribute in your controller 
    $.ajaxJsonAntiforgery = function (settings) { 

     var headers = {}; 
     headers['__RequestVerificationToken'] = $.getAntiForgeryToken(); 

     settings.dataType = 'json'; 
     settings.contentType = 'application/json; charset=utf-8'; 
     settings.type = 'POST'; 
     settings.cache = false; 
     settings.headers = headers; 
     return $.ajax(settings); 
    }; 
})(jQuery); 

Simplemente pone su token de verificación en los encabezados. También necesita un atributo de filtro para verificar su token antiforgery. Aquí está:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Helpers; 
using System.Web.Mvc; 

namespace MyProject.Web.Infrastructure.Filters 
{ 

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, 
        AllowMultiple = false, Inherited = true)] 
    public sealed class ValidateJsonAntiForgeryTokenAttribute 
           : FilterAttribute, IAuthorizationFilter 
    { 
     public void OnAuthorization(AuthorizationContext filterContext) 
     { 
      if (filterContext == null) 
      { 
       throw new ArgumentNullException("filterContext"); 
      } 

      var httpContext = filterContext.HttpContext; 
      var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName]; 
      AntiForgery.Validate(cookie != null ? cookie.Value : null, 
           httpContext.Request.Headers["__RequestVerificationToken"]); 
     } 
    } 
} 

En su controlador es muy fácil, basta con marcarlo con nuevo atributo (ValidateJsonAntiForgeryToken):

[Authorize, HttpPost, ValidateJsonAntiForgeryToken] 
public ActionResult Index(MyViewModel viewModel) 

Y en el lado del cliente:

$.ajaxJsonAntiforgery({ 
    data: dataToSave, 
    success: function() { alert("success"); }, 
    error: function() { alert("error"); } 
}); 

funciona para mi. ¡Disfrutar!

+1

Eso funcionó como un champ @ roman-pushkin. Gracias por postear tu solución. –

+0

Después de un tiempo, descubrí que "__RequestVerificationToken" no es un buen nombre. Es mejor elegir "X-RequestVerificationToken", por lo que los servidores proxy dejarán pasar este encabezado. –

0

En realidad, lo siguiente funcionó para mí con un objeto complejo;

var application = { 
    Criteria: { 
     ReferenceNumber: $("input[name='Criteria.ReferenceNumber'").val() 
    }, 
    AppliedVia: "Office" 
}; 

// get the next step 
$.ajax({ 
    url: form.attr("action"), 
    dataType: "html", 
    type: "POST", 
    data: { 
     __RequestVerificationToken: $("input[name=__RequestVerificationToken]").val(), 
     application: application 
    }, 
} 

Una cosa de la nota sin embargo, es asegurarse de que la izquierda application en data debe ser el nombre del parámetro real en su método/acción. Esto funciona a partir de MVC 5 (pre .NET Core)

Cuestiones relacionadas