ASP.NET의 필터 및 모델 바인딩 예제에 대한 자세한 설명

Y2J
풀어 주다: 2017-05-11 10:38:12
원래의
1608명이 탐색했습니다.

이 글에서는 주로 ASP.NET MVC의 공통 확장 포인트인 필터와 모델 바인딩을 자세히 소개합니다. 도움이 필요한 친구들이 참고할 수 있습니다.

1. 필터(Filter)

ASP.NET MVC의 모든 요청은 해당 컨트롤러(이하 "메소드") 아래의 특정 작업(이하 "메소드")에 할당됩니다. "Controller") ) 처리, 일반적인 상황에서는 메서드에 직접 코드를 작성하면 되지만, 메서드 실행 전후에 일부 로직을 처리하려면 여기에서 필터를 사용해야 합니다.

일반적으로 사용되는 세 가지 필터는 Authorize(권한 부여 필터), HandleError(예외 필터), ActionFilter(사용자 정의 필터)이며 해당 클래스는 AuthorizeAttribute, HandleErrorAttribute 및 ActionFilterAttribute입니다. 상속 이러한 클래스를 사용하고 해당 메서드를 재정의하여 다양한 기능을 수행합니다.

1. Authorize 인증 필터

이름에서 알 수 있듯이 인증 필터는 메소드가 실행되기 전에 실행되어 인증 여부를 제한합니다. 요청은 이 메소드를 입력할 수 있으며, 새 메소드를 생성합니다:

public JsonResult AuthorizeFilterTest()
{
 return Json(new ReturnModel_Common { msg = "hello world!" });
}
로그인 후 복사

결과에 직접 액세스:

이제 이 AuthorizeFilterTest 메소드가 백그라운드 메소드라고 가정하면 사용자는 유효한 토큰을 가지고 있어야 접근할 수 있습니다. 일반적인 방법은 AuthorizeFilterTest 메서드에서 토큰을 받아 확인하는 것입니다. 그러나 메서드가 너무 많으면 각 메서드에 확인 코드를 작성하는 것은 명백히 비현실적입니다. 인증 필터를 사용해야 합니다.

public class TokenValidateAttribute : AuthorizeAttribute
  {
    /// <summary>
    /// 授权验证的逻辑处理。返回true则通过授权,false则相反
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
      string token = httpContext.Request["token"];
      if (string.IsNullOrEmpty(token))
      {
        return false;
      }
      else
      {
        return true;
      }
    }
  }
로그인 후 복사

AuthorizeAttribute를 상속하고 AuthorizeCore 메서드를 재정의하는 새 클래스를 생성했습니다. 이 의사 코드는 토큰에 값이 있으면 true를 반환한다는 것입니다. false를 반환합니다. 액세스 메서드:

[TokenValidate]
public JsonResult AuthorizeFilterTest()
{
  return Json(new ReturnModel_Common { msg = "hello world!" })
}
로그인 후 복사

TokenValidate를 표시한 후 AuthorizeFilterTest가 true를 반환하면 승인이 AuthorizeFilterTest에서 코드를 성공적으로 실행합니다. . 토큰 전달 안 함:

토큰 전달:

토큰 전달 안 함 인증이 실패하면 MVC가 기본값으로 승인되지 않습니다. 페이지로 들어갑니다. 여기에서 개선점을 만들어 보겠습니다. 인증 성공 여부에 관계없이 반환 값 형식은 프런트 엔드 처리를 용이하게 하기 위해 일관되어야 합니다. 이때 AuthorizeAttribute 클래스의 HandleUnauthorizedRequest 메서드를 다시 작성하면 됩니다.

/// <summary>
/// 授权失败处理
/// </summary>
/// <param name="filterContext"></param>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
  base.HandleUnauthorizedRequest(filterContext);

  var json = new JsonResult();
  json.Data = new ReturnModel_Common
  {
    success = false,
    code = ReturnCode_Interface.Token过期或错误,
    msg = "token expired or error"
  };
  json.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
  filterContext.Result = json;
}
로그인 후 복사

효과:

실용: 가장 널리 사용되는 인증 필터 적용은 사용자 로그인 후권한 관리 시스템입니다. > 성공적으로 서버는 암호화된 토큰을 출력합니다. 이후 모든 요청은 이 토큰을 가져옵니다. 서버는 사용자 ID에 따라 권한이 있는지 확인하기 위해 AuthorizeCore 메서드에서 토큰을 잠금 해제합니다. 현재 인터페이스 를 요청합니다. 그렇다면 true를 반환하고, 그렇지 않으면 false를 반환합니다. 쿠키, 세션에 성공적으로 로그인하는 것과 비교하여 이 인증 방법을 사용하면 하나의 인터페이스를 PC 측과 앱 측에서 모두 사용할 수 있다는 장점이 있습니다.

2. HandleError 예외 필터

예외 필터는 코드 예외를 처리하며 MVC에서 기본적으로 예외 필터링을 구현하여 등록했을 때 실행됩니다. App_Start 디렉터리의 FilterConfig.cs:

filters.Add(new HandleErrorAttribute());
로그인 후 복사

이는 전체 시스템에 적용됩니다. 모든 인터페이스 또는 페이지 오류는 MVC의 기본

예외 처리를 실행하고 기본 오류 페이지인 Views/Shared를 반환합니다. /오류(오류를 보고하기 위해 프로그램이 서버로 전송될 때만 이 페이지를 볼 수 있습니다. 로컬 디버깅에 높은 권한이 있는 경우에도 특정 오류 정보를 볼 수 있습니다)

@{
  Layout = null;
}
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta name="viewport" content="width=device-width" />
  <title>错误</title>
</head>
<body>
  <hgroup>
    <h1>错误。</h1>
    <h2>处理你的请求时出错。</h2>
  </hgroup>
</body>
</html>
로그인 후 복사

기본 예외 필터는 분명히 사용 요구 사항을 충족할 수 없습니다. 예외 필터는 프로젝트의 실제 요구 사항을 충족하도록 다시 작성되었습니다.

1) 오류를 보고할 때 오류가 발생하는 컨트롤러와 메서드를 기록할 수 있습니다. 오류 코드는 물론 오류 보고 시

요청 매개변수 및 시간

2) 프런트엔드 처리를 용이하게 하기 위해 특정 형식으로 JSON을 반환합니다. 이제 대부분의 시스템이 Ajax 요청을 사용하기 때문에 오류가 보고되면 MVC 기본 오류 페이지로 돌아가는데, 이는 프런트엔드에서 처리하기 어렵습니다.

HandleErrorAttribute를 상속하는 새 클래스 LogExceptionAttribute를 만듭니다. 내부 OnException 메서드를 재정의합니다.

 public override void OnException(ExceptionContext filterContext)
 {
   if (!filterContext.ExceptionHandled)
   {
     string controllerName = (string)filterContext.RouteData.Values["controller"];
     string actionName = (string)filterContext.RouteData.Values["action"];
     string param = Common.GetPostParas();
     string ip = HttpContext.Current.Request.UserHostAddress;
     LogManager.GetLogger("LogExceptionAttribute").Error("Location:{0}/{1} Param:{2}UserIP:{3} Exception:{4}", controllerName, actionName, param, ip, filterContext.Exception.Message);

     filterContext.Result = new JsonResult
     {
       Data = new ReturnModel_Common { success = false, code = ReturnCode_Interface.服务端抛错, msg = filterContext.Exception.Message },
       JsonRequestBehavior = JsonRequestBehavior.AllowGet
     };
   }
   if (filterContext.Result is JsonResult)
     filterContext.ExceptionHandled = true;//返回结果是JsonResult,则设置异常已处理
   else
     base.OnException(filterContext);//执行基类HandleErrorAttribute的逻辑,转向错误页面
 }
로그인 후 복사

异常过滤器就不像授权过滤器一样标注在方法上面了,直接到App_Start目录下的FilterConfig.cs注册下,这样所有的接口都可以生效了:

filters.Add(new LogExceptionAttribute());
로그인 후 복사

异常过滤器里使用了NLog作为日志记录工具,Nuget安装命令:

Install-Package NLog
Install-Package NLog.Config
로그인 후 복사

相比Log4net,NLog配置简单,仅几行代码即可,NLog.config:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <targets>
  <target xsi:type="File" name="f" fileName="${basedir}/log/${shortdate}.log" layout="${uppercase:${level}} ${longdate} ${message}" />
  <target xsi:type="File" name="f2" fileName="D:\log\MVCExtension\${shortdate}.log" layout="${uppercase:${level}} ${longdate} ${message}" />
 </targets>
 <rules>
  <logger name="*" minlevel="Debug" writeTo="f2" />
 </rules>
</nlog>
로그인 후 복사

如果报错,日志就记录在D盘的log目录下的MVCExtension目录下,一个项目一个日志目录,方便管理。全部配置完成,看下代码:

public JsonResult HandleErrorFilterTest()
{
  int i = int.Parse("abc");
  return Json(new ReturnModel_Data { data = i });
}
로그인 후 복사

字符串强转成int类型,必然报错,页面响应:

同时日志也记录下来了:

3.ActionFilter自定义过滤器

自定义过滤器就更加灵活了,可以精确的注入到请求前、请求中和请求后。继承抽象类ActionFilterAttribute并重写里面的方法即可:

public class SystemLogAttribute : ActionFilterAttribute
{
  public string Operate { get; set; }

  public override void OnActionExecuted(ActionExecutedContext filterContext)
  {
    filterContext.HttpContext.Response.Write("<br/>" + Operate + ":OnActionExecuted");
    base.OnActionExecuted(filterContext);
  }

  public override void OnActionExecuting(ActionExecutingContext filterContext)
  {
    filterContext.HttpContext.Response.Write("<br/>" + Operate + ":OnActionExecuting");
    base.OnActionExecuting(filterContext);
  }

  public override void OnResultExecuted(ResultExecutedContext filterContext)
  {
    filterContext.HttpContext.Response.Write("<br/>" + Operate + ":OnResultExecuted");
    base.OnResultExecuted(filterContext);
  }

  public override void OnResultExecuting(ResultExecutingContext filterContext)
  {
    filterContext.HttpContext.Response.Write("<br/>" + Operate + ":OnResultExecuting");
    base.OnResultExecuting(filterContext);
  }
}
로그인 후 복사

这个过滤器适合做系统操作日志记录功能:

[SystemLog(Operate = "添加用户")]
public string CustomerFilterTest()
{
  Response.Write("<br/>Action 执行中...");
  return "<br/>Action 执行结束";
}
로그인 후 복사

看下结果:

四个方法执行顺序:OnActionExecuting—>OnActionExecuted—>OnResultExecuting—>OnResultExecuted,非常精确的控制了整个请求过程。

实战中记录日志过程是这样的:在OnActionExecuting方法里写一条操作日志到数据库里,全局变量存下这条记录的主键,到OnResultExecuted方法里说明请求结束了,这个时候自然知道用户的这个操作是否成功了,根据主键更新下这条操作日志的是否成功字段。

二、模型绑定(ModelBinder)

先看一个普通的方法:

public ActionResult Index(Student student)
{
  return View();
}
로그인 후 복사

这个方法接受的参数是一个Student对象,前端传递过来的参数跟Student对象里的属性保持一直,那么就自动被绑定到这个对象里了,不需要在方法里new Student这个对象并挨个绑定属性了,绑定的过程由MVC中的DefaultModelBinder完成的,DefaultModelBinder同时继承了IModelBinder接口,现在就利用IModelBinder接口和DefaultModelBinder来实现更加灵活的模型绑定。

场景一、前端传过来了一个加密的字符串token,方法里需要用token里的某些字段,那就得在方法里接收这个字符串、解密字符串、转换成对象,这样一个方法还好说,多了的话重复代码非常多,就算提取通用方法,还是要在方法里调用这个通用方法,有没有办法直接在参数里就封装好这个对象?

模型绑定的对象:

public class TokenModel
{
  /// <summary>
  /// 主键
  /// </summary>
  public int Id { get; set; }

  /// <summary>
  /// 姓名
  /// </summary>
  public string Name { set; get; }

  /// <summary>
  /// 简介
  /// </summary>
  public string Description { get; set; }

}
로그인 후 복사

新建一个TokenBinder继承IModelBinder接口并实现其中的BindModel方法:

public class TokenBinder : IModelBinder
{
  public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
  {
    var token = controllerContext.HttpContext.Request["token"];
    if (!string.IsNullOrEmpty(token))
    {
      string[] array = token.Split(&#39;:&#39;);
      if (array.Length == 3)
      {
        return new TokenModel() { Id = int.Parse(array[0]), Name = array[1], Description = array[2] };
      }
      else
      {
        return new TokenModel() { Id = 0 };
      }
    }
    else
    {
      return new TokenModel() { Id = 0 };
    }
  }
}
로그인 후 복사

这个方法里接收了一个token参数,并对token参数进行了解析和封装。代码部分完成了需要到Application_Start方法里进行下注册:

ModelBinders.Binders.Add(typeof(TokenModel), new TokenBinder());
로그인 후 복사

现在模拟下这个接口:

public JsonResult TokenBinderTest(TokenModel tokenModel)
{
  var output = "Id:" + tokenModel.Id + ",Name:" + tokenModel.Name + ",Description:" + tokenModel.Description;
  return Json(new ReturnModel_Common { msg = output });
}
로그인 후 복사

调用下:

可以看出,“1:汪杰:oppoic.cnblogs.com”已经被绑定到tokenModel这个对象里面了。但是如果稍复杂的模型绑定IModelBinder就无能为力了。

场景二、去除对象某个属性的首位空格

public class Student
{
  public int Id { get; set; }

  public string Name { get; set; }

  public string Class { get; set; }
}
로그인 후 복사

如果前端传来的Name属性有空格,如何去除呢?利用DefaultModelBinder即可实现更灵活的控制

public class TrimModelBinder : DefaultModelBinder
{
  protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
  {
    var obj = base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
    if (obj is string && propertyDescriptor.Attributes[typeof(TrimAttribute)] != null)//判断是string类型且有[Trim]标记
    {
      return (obj as string).Trim();
    }
    return obj;
  }
}
로그인 후 복사

标注下需要格式化首位属性的实体:

[ModelBinder(typeof(TrimModelBinder))]
public class Student
{
  public int Id { get; set; }

  [Trim]
  public string Name { get; set; }

  public string Class { get; set; }
}
로그인 후 복사

好了,测试下:

public JsonResult TrimBinderTest(Student student)
{
  if (string.IsNullOrEmpty(student.Name) || string.IsNullOrEmpty(student.Class))
  {
    return Json(new ReturnModel_Common { msg = "未找到参数" });
  }
  else
  {
    return Json(new ReturnModel_Common { msg = "Name:" + student.Name + ",长度:" + student.Name.Length + " Class:" + student.Class + ",长度:" + student.Class.Length });
  }
}
로그인 후 복사

可见,标注了Trim属性的Name长度是去除空格的长度:7,而没有标注的Class属性的长度则是6。

【相关推荐】

1. ASP.NET免费视频教程

2. ASP.NET教程

3. 极客学院ASP,NET视频教程

위 내용은 ASP.NET의 필터 및 모델 바인딩 예제에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿