在Asp.net MVC 3 Web开发中,我们会大量使用各种ajax请求,针对ajax请求如何结何server端如何做异常处理呢?我们可以扩展ActionFilterAttribute,实现一个Ajax异常处理特性。假设您是使用JQuery脚本开发来实现Ajax,看代码:
1: #region AjaxExceptionAttribute
2: /// <summary>
3: /// Ajax Exception Handle Attribute
4: /// </summary>
5: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
6: public class AjaxExceptionAttribute : ActionFilterAttribute, IExceptionFilter
7: {
8: /// <summary>
9: /// Called when an exception occurs.
10: /// </summary>
11: /// <param name="filterContext">The filter context.</param>
12: public void OnException(ExceptionContext filterContext)
13: {
14: if (!filterContext.HttpContext.Request.IsAjaxRequest())
15: return;
16:
17: filterContext.Result = AjaxError(filterContext.Exception.Message, filterContext);
18:
19: //Let the system know that the exception has been handled
20: filterContext.ExceptionHandled = true;
21: }
22:
23: /// <summary>
24: /// Ajaxes the error.
25: /// </summary>
26: /// <param name="message">The message.</param>
27: /// <param name="filterContext">The filter context.</param>
28: /// <returns>JsonResult</returns>
29: protected JsonResult AjaxError(string message, ExceptionContext filterContext)
30: {
31: //If message is null or empty, then fill with generic message
32: if (String.IsNullOrEmpty(message))
33: message = "Something went wrong while processing your request. Please refresh the page and try again.";
34:
35: //Set the response status code to 500
36: filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
37:
38: //Needed for IIS7.0
39: filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
40:
41: return new JsonResult
42: {
43: //can extend more properties
44: Data = new AjaxExceptionModel () { ErrorMessage = message },
45: ContentEncoding = System.Text.Encoding.UTF8,
46: JsonRequestBehavior = JsonRequestBehavior.DenyGet
47: };
48: }
49: }
50: #endregion
51:
52: /// <summary>
53: /// AjaxExceptionModel
54: /// </summary>
55: public class AjaxExceptionModel
56: {
57: /// <summary>
58: /// Gets or sets the error message.
59: /// </summary>
60: /// <value>
61: /// The error message.
62: /// </value>
63: public string ErrorMessage { get; set; }
64: }
代码已有相关注释。AjaxExceptionModel您完全可以自定义更多的属性,然后在Controller或Action上增加这个特性。 像下面这个HomeController有一个YouKnow的Action,这里为演示我们故意throw一个Exception:
[AjaxException]
public class HomeController : BaseController
{
[HttpPost]
public ActionResult YouKnow(FormCollection fc)
{
throw new ArgumentNullException("YouKnow method: fc should not be null");
}
}
接下来看HTML与JQuery:
<h4 id="lblMethodError">
</h4>
<br />
<h4 id="lblMethodSuccess">
</h4>
<br />
<h2>ajax post</h2>
<input type="button" value="Post method" id="inputajax1" />
<br />
<input type="button" value="Ajax method with error section" id="inputajax2" />
<p>
<input type="button" value="Post method for undefined Url" id="inputajax3" />
</p>
<p>
<input type="button" value="Post method to get Json Result" id="inputajax4" />
</p>
<br />
<h3 id="errorh3info">
</h3>
$("#errorh3info").ajaxError(function (event, request, settings) {
$(this).append("<li>settings.url:" + settings.url + "</li>");
$(this).append("<li>request.status:" + request.status + "</li>");
$(this).append("<li>request.statusText:" + request.statusText + "</li>");
//request.responseText
if (request.responseText != "") {
var jsonValue = jQuery.parseJSON(request.responseText);
$(this).append("<li>ErrorMessage:" + jsonValue.ErrorMessage + "</li>");
}
});
给button增加一个Post请求:
$('#inputajax1').click(function () {
$.post('/Home/YouKnow/'
, { id: 1 }
, function (data) {
$('div#right-box.data').html(data);
});
});
这时View上将显示, 我们将看到ErrorMessage是JSON对象的属性:
当然也可以这样:
//ajax post when throw an exception at server side
$('#inputajax2').click(function () {
$.ajax({
type: "POST",
url: "/Home/YouKnow2",
data: "fail=1",
dataType: "json",
error: function (xhr, status, error) {
// Show the error
$('#lblMethodError').append(xhr.responseText);
$('#lblMethodError').show();
},
success: function (data, textSuccess) {
// show the success message
$('#lblMethodSuccess').show();
}
});
});
这时View上,比上面那个请求多显示一条信息, 这是因为我们这里定义了自己error处理callback.
当我们请求一个不存在Action时呢:
//request undefined url
$('#inputajax3').click(function () {
$.post('/Home/YouKnow2233/'
, { id: 1 }
, function (data) {
$('div#right-box.data').html(data);
});
});
页面上将显示:
最后我们来看这个ActionFilter的单元测试,这里使用Moq类库:
[TestMethod]
public void Test_Native_AjaxExceptionAttribute()
{
//arrange
var header = new System.Collections.Specialized.NameValueCollection();
header.Add("X-Requested-With", "XMLHttpRequest");
var mockRequest = new Mock<HttpRequestBase>();
mockRequest.SetupGet(http => http.Headers).Returns(header);
var mockResponse = new Mock<HttpResponseBase>();
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.SetupGet(c => c.Request).Returns(mockRequest.Object);
mockHttpContext.SetupGet(c => c.Response).Returns(mockResponse.Object);
var context = mockHttpContext.Object;
var exceptonContextMock = new Mock<ExceptionContext>();
exceptonContextMock.SetupGet(ac => ac.HttpContext).Returns(context);
string errorStr="error";
exceptonContextMock.SetupGet(ec => ec.Exception).Returns(new AggregateException(errorStr));
//act
var ajaxAttribute = new AjaxExceptionAttribute();
var exceptionContext = exceptonContextMock.Object;
ajaxAttribute.OnException(exceptionContext);
//verify
mockRequest.VerifyAll();
mockResponse.VerifyAll();
mockHttpContext.VerifyAll();
exceptonContextMock.VerifyAll();
//assert
var jsonResult = exceptionContext.Result as JsonResult;
Assert.IsNotNull(jsonResult.Data);
Assert.IsTrue(exceptionContext.HttpContext.Request.IsAjaxRequest());
Assert.AreEqual((jsonResult.Data as AjaxExceptionModel).ErrorMessage, errorStr);
Assert.AreEqual(JsonRequestBehavior.DenyGet, jsonResult.JsonRequestBehavior);
Assert.AreEqual(typeof(AggregateException), exceptionContext.Exception.GetType());
}
上面的UnitTest我们Mock HttpContext,HttpRequest,HttpResponse来实现对ActionFilter的测试。
希望您Web开发有帮助。
作者:Petter Liu
出处:http://www.cnblogs.com/wintersun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
该文章也同时发布在我的独立博客中-Petter Liu Blog。