Почему GET-запросы возвращают JSON по умолчанию?

Как часть обновления ASP.NET MVC 2 Beta 2, по умолчанию запросы JSON GET запрещены. Похоже, вам нужно установить поле JsonRequestBehavior в JsonRequestBehavior.AllowGet, прежде чем возвращать объект JsonResult с вашего контроллера.

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

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

    return result;
}

В чем причина этого? Если я использую JSON GET, чтобы попытаться выполнить удаленную проверку, следует ли использовать другую технику?

Ответ 1

Причиной по умолчанию для DenyGet является MSDN со ссылкой на Блог Фила Хаака для получения дополнительной информации. Похож на уязвимость межсайтового скриптинга.

Ответ 2

HTTP GET по умолчанию отключен как часть защиты ASP.NET Cross-Site Request Forgery (CSRF/XSRF). Если ваши веб-службы принимают запросы GET, они могут быть уязвимы для сторонних сайтов, обрабатывающих запросы через теги <script /> и потенциально собирать ответ, изменяя настройки JavaScript.

Однако стоит отметить, что отключения GET-запросов недостаточно для предотвращения атак CSRF, и это единственный способ защитить вашу службу от типа атаки, описанного выше. См. Надежная защита для кросс-сайта запроса форекс для хорошего анализа различных векторов атак и способов защиты от них.

Ответ 3

У меня также возникла проблема, когда я перенес свой веб-сайт MVC из Visual Studio 2008 в Visual Studio 2010.

Ниже приведена основная aspx, у нее есть ViewData, которая вызывает контроллер категорий, чтобы заполнить ViewData [ "Категории" ] с помощью коллекции SelectList. Также существует script, чтобы вызвать контроллер подкатегории, чтобы заполнить вторую комбинацию с помощью javascript. Теперь я смог исправить это, добавив атрибут AlloGet на этот второй контроллер.

Здесь aspx и 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" />
<%}%>                

здесь мой контроллер для подкатегорий

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

    }

Ответ 4

Я не знаю, является ли это причиной того, что они решили изменить это значение по умолчанию, но вот мой опыт:

Когда некоторые браузеры видят GET, они думают, что могут кэшировать результат. Поскольку AJAX обычно используется для небольших запросов, чтобы получать самую последнюю информацию с сервера, кеширование этих результатов обычно приводит к неожиданному поведению. Если вы знаете, что данный вход будет возвращать один и тот же результат каждый раз (например, "пароль" не может использоваться в качестве пароля, независимо от того, когда вы спрашиваете меня), тогда GET будет прекрасным, и кеширование браузера может фактически повысить производительность в случае кто-то пытается несколько раз проверять один и тот же ввод. Если, с другой стороны, вы ожидаете другого ответа в зависимости от текущего состояния данных на стороне сервера ( "myfavoriteusername", возможно, было доступно 2 минуты назад, но оно было принято с тех пор), вы должны использовать POST, чтобы избежать браузер считает, что первый ответ по-прежнему является правильным.