В нашем MVC3 веб-приложении мы используем формс-авторизацию. В контроллере Account есть акшн Logon (принимает креденшинал пользователя), призванный залогинить пользователя. Мы хотим на стороне клиента выдавать диалог логина без смены страницы. Собственно для логина тогда использовать этот акшин Logon (точнее его post часть). В акшине мы определяем что это ajax запрос и если логин некорректный — возвращаем статус код возврата 401. Но на стороне браузера вместо получения error с кодом ошибки 401 мы получаем контент страницы с логином.
Вопрос такой, как сделать чтобы post акшин Logon в случае ajax запроса и неверных данных логина возвращал статус код ошибки — 401 ? а не редиректил на страницу Logon-а...
код акшина вот:
[HttpPost]
public ActionResult LogOn(Credentials member)
{
if (ModelState.IsValid)
{
if (!Membership.ValidateUser(member.UserName, member.Password))
{
if (Request.IsAjaxRequest())
{ //в кач-ве результата хотим выдать только статус код. !!!! но asp.net упорно редиректит на страницу Logon-а :crash: return new new HttpStatusCodeResult(401);
}
................. поскипано
Не пробовали в настройках ajax-запроса указать тип возвращаемых данных — json, и вместо HttpStatusCodeResult(401) возвращать JsonResult и анализировать его на клиенте?
if (Request.IsAjaxRequest())
{
return new JsonResult { Data = { errorCode = "401" }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
Re[2]: как обойти дефолтный редирект на Logon?
От:
Аноним
Дата:
06.02.12 06:59
Оценка:
Здравствуйте, Sufflavus, Вы писали:
S>Не пробовали в настройках ajax-запроса указать тип возвращаемых данных — json, и вместо HttpStatusCodeResult(401) возвращать JsonResult и анализировать его на клиенте? S>
Спасибо за идею попробовали, действительно редирект непроисходит!
Но, хотелось бы понять почему в этом случае нет редиректа и есть в случае когда мы сразу возвращаем нужный статус код...
И всё таки хотелось бы в случае успешного логина возвращать статус код=200 с сообщением об успешности. А в случае неуспешного возвращать 401 и сообщение об неуспешности. Такое поведение мне кажется лучше отражает суть происходящего. Предложенный способ позволяет победить редирект, но вынуждает изменить семантику метода (в случае неуспеха возвращать статус код) и выглядит как workaround...
$.ajax({
url: "http://...",
data: {},
complete: function(xhr, statusText){
alert(xhr.status); // в случае ошибки авторизации должно быть 401
// скорее всего, statusText равен 'parsererror', если ошибка авторизации, и "success" - если все ок
}
error: function(xhr, statusText, errorThrown)
{
alert(xhr.status); // в случае ошибки авторизации должно быть 401
// скорее всего, statusText равен 'parsererror' в случае ошибки авторизации
}
});
Re[4]: как обойти дефолтный редирект на Logon?
От:
Аноним
Дата:
06.02.12 08:06
Оценка:
Здравствуйте, Sufflavus, Вы писали:
S>Можно попробовать отловить ошибку на клиенте.
.... S>или как-то так S>
S>$.ajax({
S> url: "http://...",
S> data: {},
S> complete: function(xhr, statusText){
S> alert(xhr.status); // в случае ошибки авторизации должно быть 401
S> // скорее всего, statusText равен 'parsererror', если ошибка авторизации, и "success" - если все ок
S> }
S> error: function(xhr, statusText, errorThrown)
S> {
S> alert(xhr.status); // в случае ошибки авторизации должно быть 401
S> // скорее всего, statusText равен 'parsererror' в случае ошибки авторизации
S> }
S>});
S>
фигня в том что, в рез-те того что акшн возвращает редирект недра $.ajax автоматом дергают url редиректа и мы попадаем всегда на success (т.к. статус всегда=200, т.к. запрос на редирект выполненный атоматом действительно — ок).
т.е. $.ajax автоматом обрабатывает редирект...
Здравствуйте, <Аноним>, Вы писали:
А>В нашем MVC3 веб-приложении мы используем формс-авторизацию. В контроллере Account есть акшн Logon (принимает креденшинал пользователя), призванный залогинить пользователя. Мы хотим на стороне клиента выдавать диалог логина без смены страницы. Собственно для логина тогда использовать этот акшин Logon (точнее его post часть). В акшине мы определяем что это ajax запрос и если логин некорректный — возвращаем статус код возврата 401. Но на стороне браузера вместо получения error с кодом ошибки 401 мы получаем контент страницы с логином.
401 код ошибки специальным образом обрабатывается модулем аутентфикации
public sealed class FormsAuthenticationModule : IHttpModule {
...
private void OnLeave(Object source, EventArgs eventArgs) {
////////////////////////////////////////////////////////////
// Step 1: Check if we are using cookie authentication and
// if authentication failed if (context.Response.StatusCode != 401)
return;
////////////////////////////////////////////////////////////
// Change 401 to a redirect to login page
// Don't do it if already there is ReturnUrl, already being redirected,
// to avoid infinite redirection loop
String strUrl = context.Request.RawUrl;
if (strUrl.IndexOf("?ReturnUrl=", StringComparison.Ordinal) != -1
|| strUrl.IndexOf("&ReturnUrl=", StringComparison.Ordinal) != -1) {
return;
}
////////////////////////////////////////////////////////////
// Step 2: Get the complete url to the login-page
...
...
}
}
А>Вопрос такой, как сделать чтобы post акшин Logon в случае ajax запроса и неверных данных логина возвращал статус код ошибки — 401 ? а не редиректил на страницу Logon-а...
Решение достаточно простое — вызвать Response.End() после того, как устанавлен код 401 не давая таким образом его переопределить
Response.StatusCode = 401;
Response.End();
А чтобы было как у взрослых людей, оформляем решение в соответствующий класс
public class HttpUnauthorizedAccessResult : HttpStatusCodeResult
{
// HTTP 401 is the status code for unauthorized access.public const int UnauthorizedCode = 401;
public HttpUnauthorizedAccessResult()
: this(null)
{ }
public HttpUnauthorizedAccessResult(string statusDescription)
: base(UnauthorizedCode, statusDescription)
{ }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
context.HttpContext.Response.StatusCode = StatusCode;
if (StatusDescription != null)
context.HttpContext.Response.StatusDescription = StatusDescription;
context.HttpContext.Response.End();
}
}
... << RSDN@Home 1.2.0 alpha 5 rev. 1537>>
Re[2]: как обойти дефолтный редирект на Logon?
От:
Аноним
Дата:
08.02.12 13:51
Оценка:
Здравствуйте, rameel, Вы писали:
R>401 код ошибки специальным образом обрабатывается модулем аутентфикации
R>Решение достаточно простое — вызвать Response.End() после того, как устанавлен код 401 не давая таким образом его переопределить R>
R>Response.StatusCode = 401;
R>Response.End();
R>
Супер!!! Сразу видно профи спасибо за столь точный и ясный ответ
А как вам удалось понять где и найти системный код который обрабатывает эту ситуацию ?...
Здравствуйте, <Аноним>, Вы писали:
А>А как вам удалось понять где и найти системный код который обрабатывает эту ситуацию ?...
Помешать перехватывать 401 код, используя Response.End() первое, что пришло на ум.
А код который перехватывает,.. то если бы я делал специальную обработку кодов, то написал бы модуль, тот который IHttpModule, в котором и перехватывал бы нужные мне данные. А раз так, то рефлектор подскажет в котором из них, и в котором из всего списка больше всего подходил FormsAuthenticationModule, а просмотр кода подтвердил догадку. Вот и все.
ЗЫ. При открытом рефлекторе поиск ответа на вопрос занял от силы одну минуту