• Asp.net MVC3扩展之Ajax异常处理特性


          在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>

    JQuery的全局AjaxError配置,可参考官方
    $("#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对象的属性:

  • settings.url:/Home/YouKnow/
  • request.status:500
  • request.statusText:error
  • ErrorMessage:Value cannot be null. Parameter name: YouKnow method: fc should not be null


    当然也可以这样:

  • //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.

  • {"ErrorMessage":"Value cannot be null.\r\nParameter name: YouKnow method: fc should not be null"}

    当我们请求一个不存在Action时呢:

    //request undefined url
    $('#inputajax3').click(function () {
        $.post('/Home/YouKnow2233/'
       , { id: 1 }
       , function (data) {
           $('div#right-box.data').html(data);
       });
    });


    页面上将显示:

  • settings.url:/Home/YouKnow2233/
  • request.status:0
  • request.statusText:error

  • 最后我们来看这个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

  • 相关阅读:
    使用Beautiful Soup扒取指定标题
    平时笔记
    2017年暑期大数据培训小记
    ZoomIt的使用方法
    ASP.NET使用富文本控件KindEditor(一步到位,亲测有效)
    ASP.NET使用Ajax返回Json对象
    Python简易爬虫
    Class.forName()用法详解
    【4-1】js函数、事件、补充知识
    【3-30】document获取、事件、标记样式
  • 原文地址:https://www.cnblogs.com/wintersun/p/2291800.html
Copyright © 2020-2023  润新知