2009-10-26 21 views
32

Como parte de la actualización ASP.NET MVC 2 Beta 2, las solicitudes GET JSON no están permitidas por defecto. Parece que debe establecer el campo JsonRequestBehavior en JsonRequestBehavior.AllowGet antes de devolver un objeto JsonResult desde su controlador.¿Por qué las solicitudes GET devuelven JSON no permitido por defecto?

public JsonResult IsEmailValid(...) 
{ 
    JsonResult result = new JsonResult(); 

    result.Data = ..... ; 
    result.JsonRequestBehavior = JsonRequestBehavior.AllowGet; 

    return result; 
} 

¿Cuál es el razonamiento detrás de esto? Si estoy usando JSON GET para intentar hacer una validación remota, ¿debería usar una técnica diferente?

+2

es "JsonRequestBehavior" propiedad se han añadido sólo en MVC2. Becoz Intenté buscar esto en mvc 1.0 y no pude averiguarlo. – Santhosh

+0

Sí, se agregó en v2. Al menos, los documentos 1.0 aquí (http://msdn.microsoft.com/en-us/library/system.web.mvc.jsonresult_members.aspx) no lo enumeran. – Jedidja

+0

gracias por la información. – Santhosh

Respuesta

22

El motivo del valor predeterminado de DenyGet es MSDN con un enlace a Phil Haack's blog para obtener más información. Parece una vulnerabilidad de scripting entre sitios.

+0

entonces si yo JsonRequestBehavior.AllowGet ...¿Esta vulnerabilidad continuará existiendo o produce alguna protección o qué? Si necesito devolver los datos de Json, devolvería los datos de Json sin que Explicity me repita ??? !!!! –

+1

@jalchr: utilizando AllowGet la vulnerabilidad seguirá existiendo, solo le está diciendo al framework que no le importa (es decir, está de acuerdo con eso, porque no envía datos confidenciales) –

+0

es una vulnerabilidad de CSRF, no Cross -Sitio de scripting! – roo2

2

No sé si esta es la razón por la que optó por cambiar ese defecto, pero aquí está mi experiencia:

Cuando algunos navegadores ver un GET, que piensan que pueden almacenar en caché el resultado. Dado que AJAX se usa generalmente para pequeñas solicitudes para obtener la información más actualizada del servidor, el almacenamiento en caché de estos resultados generalmente termina causando un comportamiento inesperado. Si sabe que una entrada determinada arrojará el mismo resultado cada vez (por ejemplo, "contraseña" no puede usarse como contraseña, no importa cuándo me pregunte), entonces un GET está bien, y el almacenamiento en caché del navegador puede mejorar el rendimiento en caso alguien intenta validar la misma entrada varias veces. Si, por otro lado, espera una respuesta diferente según el estado actual de los datos del lado del servidor ("myfavoriteusername" puede haber estado disponible hace 2 minutos, pero se ha tomado desde entonces), debe usar POST para evitar tener el navegador piensa que la primera respuesta sigue siendo la correcta.

+0

Idea muy interesante ... ¿tiene algún vínculo con qué navegadores pueden intentar almacenar en caché un resultado GET y bajo qué circunstancias? – Jedidja

+1

El ejemplo más frecuente es Internet Explorer (http://greenash.net.au/posts/thoughts/an-ie-ajax-gotcha-page-caching), pero no se trata tanto de qué navegadores lo hacen como de hecho de que la especificación HTTP dice específicamente que los datos recuperados con una solicitud GET son almacenados en caché. Sin embargo, el marco AJAX de ASP.NET puede establecer encabezados para decirle al navegador que el contenido no se debe almacenar en caché, por lo que puede no tener nada que ver con este problema en particular. Me encontré con este problema cuando escribía mi propio par de javascript y servlets para comunicarme a través de AJAX. – StriplingWarrior

+0

Me he topado con el problema del almacenamiento en caché varias veces y el uso de POST (incluso sin tener en cuenta los problemas de seguridad) es definitivamente el camino a seguir. – Jedidja

8

HTTP GET está deshabilitado de forma predeterminada como parte de las protecciones de falsificación de solicitudes entre sitios (CSRF/XSRF) de ASP.NET. Si sus servicios web aceptan solicitudes GET, entonces pueden ser vulnerables a sitios de terceros que realizan solicitudes a través de las etiquetas <script /> y potencialmente aprovechar la respuesta modificando los programadores de JavaScript.

Sin embargo, vale la pena señalar que deshabilitar las solicitudes GET no es suficiente para evitar los ataques CSRF, ni es la única forma de proteger su servicio contra el tipo de ataque descrito anteriormente. Ver Robust Defenses for Cross-Site Request Forgery para un buen análisis de los diferentes vectores de ataque y cómo protegerse de ellos.

3

También tuve el problema cuando emigré mi página web MVC desde Visual Studio 2008 a Visual Studio 2010.

El aspx principal está por debajo, tiene una ViewData que llama a un controlador de Categoría con el fin de llenar [ViewData "Categorías"] con la colección SelectList. También hay un script para llamar a un controlador de subcategoría para completar el segundo combo con javascript. Ahora pude arreglarlo agregando el atributo AlloGet en este segundo controlador.

Aquí está el aspx y javascript

<head> 
<script type="text/javascript" src="../../Scripts/jquery-1.4.1.min.js"></script> 
<script type="text/javascript"> 
$(document).ready(function() { 
$("#CategoryId").change(function() { 

    var categoryId = $(this)[0].value; 

    $("#ctl00_MainContent_SubcategoryId").empty(); 
    $("#ctl00_MainContent_SubcategoryId").append("<option value=''>-- select a category --</option>"); 
    var url = "/Subcategory/Subcategories/" + categoryId; 

    $.getJSON(url, { "selectedItem": "" }, function (data) { 
     $.each(data, function (index, optionData) { 
      $("#ctl00_MainContent_SubcategoryId").append("<option value='" + optionData.SubcategoryId + "'>" + optionData.SubcategoryName + "</option>"); 
     }); 
     //feed our hidden html field 
     var selected = $("#chosenSubcategory") ? $("#chosenSubcategory").val() : ''; 
     $("#ctl00_MainContent_SubcategoryId").val(selected); 

    }); 

}).change(); 
}); 
</script> 
<body> 
<% using (Html.BeginForm()) {%> 
<label for="CategoryId">Category:</label></td> 
<%= Html.DropDownList("CategoryId", (SelectList)ViewData["Categories"], "--categories--") %> 
<%= Html.ValidationMessage("category","*") %> 
<br/> 
<label class="formlabel" for="SubcategoryId">Subcategory:</label><div id="subcategoryDiv"></div> 
<%=Html.Hidden("chosenSubcategory", TempData["subcategory"])%> 
<select id="SubcategoryId" runat="server"> 
</select><%= Html.ValidationMessage("subcategory", "*")%> 
<input type="submit" value="Save" /> 
<%}%>     

aquí está mi controlador de subcategorías

public class SubcategoryController : Controller 
{ 
    private MyEntities db = new MyEntities(); 

    public int SubcategoryId { get; set; } 
    public int SubcategoryName { get; set; } 
    public JsonResult Subcategories(int? categoryId) 
    { 
     try 
     { 
      if (!categoryId.HasValue) 
       categoryId = Convert.ToInt32(RouteData.Values["id"]); 
      var subcategories = (from c in db.Subcategories.Include("Categories") 
           where c.Categories.CategoryId == categoryId && c.Active && !c.Deleted 
            && c.Categories.Active && !c.Categories.Deleted 
           orderby c.SubcategoryName 
           select new { SubcategoryId = c.SubcategoryId, SubcategoryName = c.SubcategoryName } 
      ); 
      //just added the allow get attribute 
      return this.Json(subcategories, JsonRequestBehavior.AllowGet); 
     } 
     catch { return this.Json(null); } 

    } 
+0

+1 para la muestra de código. Gracias. – Rap

Cuestiones relacionadas