我正在使用ASP.NET MVC 5和WebApi 2,尝试进行Ajax登录并在成功后重定向。
为了进行身份验证,我正在使用默认情况下为新ASP.NET MVC项目提供的ASP.NET Identity库。
我已经让所有东西都运作良好,并且在使用MVC 5时一切正常。但是,我无论如何都无法使标准的MVC控制器仅返回简单的JSON。(它会将我要返回的JSON包装在一个父对象中)。是的,我可以在客户端上解决这个问题,但对我来说这是一个hacky的方法。
我的另一个选择似乎更好,那就是使用WebApi,它将对象返回为我期望的形式(只是我的JSON作为正文)。但我遇到的问题是,除非我返回一个ActionResult,否则ASP.NET Identity SignInManager
不会发送.ASPNet.Identity
cookie。
以下是我的WebApi控制器,它返回了正确预期的最小JSON,但没有发送Set-Cookie命令,因此任何重定向都会将用户视为未登录。
[Authorize]
public class AccountController : ApiController
{
public AccountController(IApplicationSignInManager signInManager)
{
SignInManager = signInManager;
}
public IApplicationSignInManager SignInManager {....}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[NgValidateAntiForgeryToken]
public async Task<HttpResponseMessage> Login(LoginViewModel model)
{
// Temporarily using Dynamic
dynamic res = new ExpandoObject();
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var status = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
res.status = status.ToString();
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(res))
};
}
}
该方法返回以下Json但不返回cookie:
{"status":"Success"}
如果我把这个改成返回ActionResult而不是HttpResponseMessage,那么Set-Cookie命令会被发送,但是Json会被包裹在额外的属性中。
public async Task<ActionResult> Login(LoginViewModel model)
{
// Temporarily using Dynamic
dynamic res = new ExpandoObject();
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var status = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
res.status = status.ToString();
return new JsonResult{Data = res};
}
这个返回cookie,但是被Json包装了:
{"contentEncoding":null,"contentType":null,"data":{"status":"Success"},"jsonRequestBehavior":1,"maxJsonLength":null,"recursionLimit":null}
现在我猜测这是因为SignInManager可能会将cookies分配给先前生成的HttpContext.Current.Response对象。当我返回JsonResult时,ASP.NET将此结果附加到HttpContext.Current.Response并发送到客户端,因此具有cookies。
但是,当我返回HttpResponseMessage时,ASP.NET返回新创建的HttpResponse,其中没有SignInManager cookies。我想这样想是正确的吗?
编辑1:根据@David Tansey的建议,我尝试了以下操作,仍未设置cookies,但返回正确的Json。
public async Task<IHttpActionResult> Login(LoginViewModel model)
{
var status = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
return Json(new {status = status.ToString()});
}
返回正确的JSON,但没有cookies:
{"status":"Success"}
修复:在@David Tansey指出使用匿名类型new {}之后,我决定尝试一下。以下两种方法都有效。
MVC
必须返回一个ActionResult/JsonResult,其中除了Data之外的所有字段都为null,并且必须返回一个匿名
类型,而不是动态的ExpandoObject()
,因为动态对象会导致序列化程序使返回的Json膨胀。
[HttpPost]
[AllowAnonymous]
[NgValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model)
{
var status = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
//return Json(new {status = status.ToString()});
// OR
return new JsonResult { Data = new { status = status.ToString() } };
}
WebApi 2
必须将返回类型更改为对象,该对象会被序列化为Json并设置cookies。如果返回HttpResponseMessage,则由SignInManager设置的cookies似乎会丢失,因为它开始使用新返回的响应对象。
[HttpPost]
[AllowAnonymous]
[NgValidateAntiForgeryToken]
public async Task<object> Login(LoginViewModel model)
{
var status = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
return new {status = status.ToString()};
}