您有关于如何使控制器动作在正常调用和通过 Ajax 时正常工作的技巧吗?这是编写动作方法时的一个恼人问题。
是的,是的,我愿意。我们还解决了一个类似的问题——我们希望应用程序有一堆表单,这些表单通常可以通过 ajax 调用,但可以正常命中。此外,我们不希望 javascript 中出现一大堆重复的逻辑。无论如何,我们想出的技术是使用一对 ActionFilterAttributes 来拦截表单,然后使用一点 javascript 来连接表单以进行 ajax 处理。
首先,对于 ajax 请求,我们只是想换出母版页:
private readonly string masterToReplace;
/// <summary>
/// Initializes an Ajax Master Page Switcharoo
/// </summary>
/// <param name="ajaxMaster">Master page for ajax requests</param>
public AjaxMasterPageInjectorAttribute(string ajaxMaster)
{
this.masterToReplace = ajaxMaster;
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest() || !(filterContext.Result is ViewResult)) return;
ViewResult vr = (ViewResult) filterContext.Result;
vr.MasterName = masterToReplace;
}
}
在返回端,我们使用 xVal 来验证客户端,因此不应该获得太多无效数据,但仍然可以获得一些。为此,我们只使用普通验证并让 aciton 方法返回带有验证消息的表单。成功的帖子通常会获得重定向奖励。无论如何,我们为成功案例做了一点 json 注入:
/// <summary>
/// Intercepts the response and stuffs in Json commands if the request is ajax and the request returns a RedirectToRoute result.
/// </summary>
public class JsonUpdateInterceptorAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
JsonResult jr = new JsonResult();
if (filterContext.Result is RedirectResult)
{
RedirectResult rr = (RedirectResult) filterContext.Result;
jr.Data = new {command = "redirect", content = rr.Url};
}
if (filterContext.Result is RedirectToRouteResult)
{
RedirectToRouteResult rrr = (RedirectToRouteResult) filterContext.Result;
VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(filterContext.RequestContext, rrr.RouteValues);
jr.Data = new {command = "redirect", content = vpd.VirtualPath};
}
if (jr.Data != null)
{
filterContext.Result = jr;
}
}
}
}
最后一个技巧是使用一个小的 javascript 对象将所有内容联系在一起:
function AjaxFormSwitcher(form, outputTarget, doValidation) {
this.doValidation = doValidation;
this.outputTarget = outputTarget;
this.targetForm = form;
}
AjaxFormSwitcher.prototype.switchFormToAjax = function() {
var afs = this;
var opts = {
beforeSubmit: this.doValidation ? afs.checkValidation : null,
complete: function(xmlHttp, status){ afs.processResult(afs, xmlHttp, status); },
clearForm: false
};
this.targetForm.ajaxForm(opts);
}
AjaxFormSwitcher.prototype.checkValidation = function(formData, jqForm, options) {
jqForm.validate();
return jqForm.valid();
}
AjaxFormSwitcher.prototype.processResult = function(afs, xmlHttp, status) {
if (xmlHttp == null) return;
var r = xmlHttp;
var c = r.getResponseHeader("content-type");
if (c.match("json") != null) {
var json = eval("(" + r.responseText + ")");
afs.processJsonRedirect(json);
}
if (c.match("html") != null) {
afs.outputTarget.html(r.responseText);
}
}
AjaxFormSwitcher.prototype.processJsonRedirect = function(data) {
if (data!=null) {
switch (data.command) {
case 'redirect':
window.location.href = data.content;
break;
}
}
}
那个小脚本处理诸如连接表单以执行 ajax 和处理结果(json 命令或显示在目标中的 html)之类的事情。诚然,我不擅长编写 javascript,所以可能有一种更优雅的方式来编写它。