四、Attribute
Attribute分多种
Attribute称为特性,语法:特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。
1、FilterAttribute(过滤器)可以看出mvc 引用的是System.Web.Mvc,webapi 引用的是System.Web.Http.Filters ,不知道小伙伴们有看出来别的区别没有,对的,有的 ,虚方法传入类不同,这样导致传入构造与输出构造也将不同了。
ActionFilterAttribute过滤器-基于Web.MVC
自定义Filter需要继承ActionFilterAttribute抽象类:例如Action控制器用于登录时候的验证有四种:分别
public class FilterAttribute : ActionFilterAttribute { public string Message { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); filterContext.HttpContext.Response.Write("Action执行之前" + Message + "<br />"); } public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); filterContext.HttpContext.Response.Write("Action执行之后" + Message + "<br />"); } public override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); filterContext.HttpContext.Response.Write("返回Result之前" + Message + "<br />"); } public override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); filterContext.HttpContext.Response.Write("返回Result之后" + Message + "<br />"); } }
然后在调用过滤器的时候,添加上该参数,Controller代码如下:
[Filter(Message="刘备")] //参数给上 public ActionResult Index() { return View(); }
另一种使用方式是进入Control执行下Action也执行一下
通过如果标签打到Controller上的话,TestFilterAttributeFilter将作用到Controller下的所有的Action。
默认情况下Action上打了某个自定义标签后,虽然在Controller上也打上了此标签,但它只有Action上的标签起作用了。
补充:如果Action没有打上该标签,那么Controller上的标签便会被执行。
如果想让Action上的标签执行一次,然后Controller上的标签也执行一次,那么应该如何操作呢?
我们只需在FilterAttribute类的定义上打上标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]即可也就是让其成为可以多次执行的Action。
[AttributeUsage(AttributeTargets.All,AllowMultiple = true)] public class FilterAttribute : ActionFilterAttribute { public string Message { get; set; } ......
有时我们想有些公共的方法需要每个Action都执行,但是又不想再每一个Controller上都打上Action标签?答案就在Global.asax中。
如果是WebAPI则是WebApiConfig.cs中配置全局。
例如:项目Session过滤
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace WebDemo { public class LoginAttribute: ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { var user = HttpContext.Current.Session["user"]; if (user==null) { filterContext.HttpContext.Response.Write("<script>top.location.href = '/Account/Index'</script>"); } } } }
使用方式
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace WebDemo.Controllers { [Login] public class HomeController : Controller { // GET: Home public ActionResult Index() { return View(); } } }
ActionFilterAttribute过滤器-基于Web.Http.Filters
WebAPI 执行Action执行后执行的过滤器
using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Web; using System.Web.Http.Filters; namespace WebApplication2.App_Start { public class VLDHousingEnquiriesAttribute :ActionFilterAttribute { public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { //抓取API控制器返回的值 注:该控制器必须是WebApi类型的控制器 /* public string index() { return "请求成功!"; } */ var data = actionExecutedContext.ActionContext.Response.Content.ReadAsAsync<object>().Result; /*即data="请求成功!"*/ if (data.ToString() != "请求成功!") { // 取得由 API 返回的状态代码 var StatusCode = actionExecutedContext.ActionContext.Response.StatusCode; //请求是否成功 var IsSuccess = actionExecutedContext.ActionContext.Response.IsSuccessStatusCode; //判断基本请求是经过action的状态标识 //结果转为自定义消息格式 var str = new { dsa = "更改action内容" }; HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str.ToString(), Encoding.GetEncoding("UTF-8"), "application/json") }; // 重新封装回传格式 actionExecutedContext.Response = result;//过滤器内部会监听内容是否被改变 return; } else { //这里不做任何更改 } } } }
执行action之前执行的过滤器
using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Web; using System.Web.Http.Controllers; using System.Web.Http.Filters; namespace Web.App_Start { public class VLDApiAttribute { } public class VLDHousingEnquiriesAttribute : ActionFilterAttribute //必须引用using System.Web.Http.Filters; { public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { /*var str = new { dsa = "dasdas" }; HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str.ToString(), Encoding.GetEncoding("UTF-8"), "application/json") }; actionContext.Response = result;*/ //如果不注释就不会执行action 因为你拿到过滤器的actionContext上下文 进行Response操作了。 //var request = HttpContext.Current.Request;var s = request["apiId"]; base.OnActionExecuting(actionContext); } } }
Executing->Action->Executed
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Web; using System.Web.Helpers; using System.Web.Http.Filters; using WebApplication2.Controllers; namespace WebApplication2.App_Start { public class VLDHousingEnquiriesAttribute : ActionFilterAttribute { public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { /*var str = new { dsa = "dasdas" }; HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str.ToString(), Encoding.GetEncoding("UTF-8"), "application/json") }; actionContext.Response = result;*/ //上面将做实体验证,判断是否返回Response值对应将执行不执行Action和Executed(Executed是跟在Action后的,Action都不执行何来的Executed) base.OnActionExecuting(actionContext); } /* //Action是WebApi类型 [VLDHousingEnquiriesAttribute] [HttpGet] public dynamic index() { return new { id = "3",name="5" }; } */ public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { var response = actionExecutedContext.Response = actionExecutedContext.Response ?? new HttpResponseMessage(); var data = response.Content.ReadAsAsync<object>().Result; if (data.ToString() == "请求成功!")//通过判断返回Response值封装请求 这里Data是json对象 返回都要转换 Json字符串传到前台 { // 取得由 API 返回的状态代码 var StatusCode = response.StatusCode; //请求是否成功 var IsSuccess = response.IsSuccessStatusCode; //结果转为自定义消息格式 var str = new { dsa = "更改action内容" }; // 重新封装回传格式 //HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str.ToString(), Encoding.GetEncoding("UTF-8"), "application/json") }; //actionExecutedContext.Response = result;//过滤器内部会监听内容是否被改变 //或者 response.Content = new StringContent(Json.Encode(str), Encoding.UTF8, "application/json");//过滤器内部会监听内容是否被改变 return; } else { //这里不做任何更改 } } } }
在OnActionExecuting中阻止后面Action的执行
filterContext.Result = new HttpNotFoundResult();//阻止后续Action的执行 原理一样改变了 Result基于 mvc的过滤器。原由是:filterContext.Result只要不为空Action就会终止。直接响应请求。
带参数的自定义Filter
还是按照之前添加自定义过滤器的方法,添加一个自定义过滤器,只是里面多了一个属性,代码如下:
public class FilterAttribute : ActionFilterAttribute { public string Message { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); filterContext.HttpContext.Response.Write("Action执行之前" + Message + "<br />"); } public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); filterContext.HttpContext.Response.Write("Action执行之后" + Message + "<br />"); } public override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); filterContext.HttpContext.Response.Write("返回Result之前" + Message + "<br />"); } public override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); filterContext.HttpContext.Response.Write("返回Result之后" + Message + "<br />"); } }
[Filter(Message="刘备")] //参数给上 public ActionResult Index() { return View(); }
输出结果如下:
AuthorizeAttribute过滤器-基于Web.Http.Filters还是MVC 看你是MVC控制器还是WebAPI
使用场景:1、未登录访问功能模块的时候 2、未登录获取分页数据 解决方式1、 父控制器继承处理 2、过滤器
1、重写Authorize有以下几个要点需注意:
1、HandleUnauthorizedRequest中基类方法已经将Response的状态设为”HttpStatusCode.Unauthorized(即401)“,重写时手请动改为”HttpStatusCode.Forbidden(即403)“,否则按401状态往下执行,就要被重定向到登录页-401错误又对应了Web.config中的
<authentication mode="Forms">
<forms loginUrl="~/" timeout="2880" />
</authentication>。
2、webApi下的授权筛选attribute为System.Web.Http.AuthorizeAttribute,而Mvc下用的是System.Web.Mvc.AuthorizeAttribute。这里别继承错了,否则授权筛选attrbute拦截不了。
3、WebApi下Authorize.HandleUnauthorizedRequest的参数filterContext在此上下文里response还为空,需要手动创建。
/// <summary> /// 重写实现处理授权失败时返回json,避免跳转登录页 /// </summary> public class ApiAuthorize : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(HttpActionContext filterContext) { base.HandleUnauthorizedRequest(filterContext); var response = filterContext.Response = filterContext.Response ?? new HttpResponseMessage(); response.StatusCode = HttpStatusCode.Forbidden; var content = new Result { success = false, errs = new[] { "服务端拒绝访问:你没有权限,或者掉线了" } }; response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json"); } }
为防止上下文为null
var response = filterContext.Response = filterContext.Response ?? new HttpResponseMessage(); 或者 if (filterContext == null ) { throw new ArgumentNullException( "filterContext" ); }else { string path = context.HttpContext.Request.Path; string strUrl = "/Account/LogOn?returnUrl={0}" ; context.HttpContext.Response.Redirect( string .Format(strUrl, HttpUtility.UrlEncode(path)), true ); }
chrome下查看返回状态:
还有一种方式
跨域攻击---自然来路页面和目标页面不在同一个域下,所以直接判断来路域和当前自己的域就可以了。
比如内嵌Iframe或者Ajax调用的
代码顺序为:OnAuthorization-->AuthorizeCore-->HandleUnauthorizedRequest
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace Admin.MyAttribute { [AttributeUsage(AttributeTargets.All, Inherited = true)] public class CheckAuthority : AuthorizeAttribute { protected override bool AuthorizeCore(HttpContextBase httpContext) { bool Pass = true; Uri UrlReferrer = httpContext.Request.UrlReferrer;//获取来路 if (UrlReferrer == null) { httpContext.Response.StatusCode = 401;//无权限状态码 Pass = false; } else { Uri ThisUrl = httpContext.Request.Url;//当前请求的URL if (UrlReferrer.Authority != ThisUrl.Authority) { httpContext.Response.StatusCode = 401;//无权限状态码 Pass = false; } } return Pass; } protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { base.HandleUnauthorizedRequest(filterContext); if (filterContext.HttpContext.Response.StatusCode == 401) filterContext.Result = new RedirectResult("/"); } } }
AuthorizeAttribute的OnAuthorization方法内部调用了AuthorizeCore方法,这个方法是实现验证和授权逻辑的地方,如果这个方法返回true,
表示授权成功,如果返回false, 表示授权失败, 会给上下文设置一个HttpUnauthorizedResult,这个ActionResult执行的结果是向浏览器返回。
关于 HttpUnauthorizedResult 测试 结果可以这样写 filterContext.Result = new HttpUnauthorizedResult("no authentication");
调用方法
[MyAttribute.CheckAuthority]--先验证,通过验证才执行 public ActionResult Index() { return View(); }
当我们因不想每个控制器都添加授权属性的时候,而在全局配置文件内注册后,如果有些控制器的action方法不需要验证,则在action上添加属性[AllowAnonymous]
注意:
WebApi层接口效果示例
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Web; using System.Web.Helpers; using System.Web.Http.Controllers; using System.Web.Http.Filters; using Web.Common.Method; using Web.Common.ReqResult; using WebBLL; using WebCommon; namespace Web.App_Start { public class SignVerifyAttribute : ActionFilterAttribute { private static readonly int ApiMinutes = Convert.ToInt32(ConfigHelper.GetConfigValue("ApiMinutes")); //请求超时时间 private static readonly IList<string> BaseParamKey = new List<string>() { "apiId" //必填-用户标识 , "timeStamp"//必填-时间戳[单位毫秒] , "nonce_Str"//必填-随机字符串 , "sign" //必填-签名 }; public override void OnActionExecuting(HttpActionContext actionContext) { try { var context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"]; var param = new NameValueCollection(); var method = context.Request.HttpMethod.ToUpperInvariant(); param = method.Equals("GET", StringComparison.OrdinalIgnoreCase) ? context.Request.QueryString : context.Request.Form; #region 判断基础参数是否存在 foreach (var item in BaseParamKey) { if (!param.AllKeys.Any(x => x.Trim().Equals(item, StringComparison.OrdinalIgnoreCase))) { var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage(); var content = new ReqResult<string> { code = Convert.ToString((int)SatausCode.CommonCode.incomplete), message = item + SatausCode.GetDisplayDescription(SatausCode.CommonCode.incomplete), result = null }; response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json"); return; } } #endregion #region 验证时间戳格式&&请求时间 var currentTimeStamp = OperData.GetTmeStamp(DateTime.Now); var getTimeStamp = param["timestamp"]; long getIntTimeStamp = 0; if (!long.TryParse(getTimeStamp, out getIntTimeStamp)) { var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage(); var content = new ReqResult<string> { code = Convert.ToString((int)SatausCode.CommonCode.timeStamp), message = SatausCode.GetDisplayDescription(SatausCode.CommonCode.timeStamp), result = null }; response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json"); return; } //请求时间默认不能超过5分钟 if (Convert.ToInt64(currentTimeStamp) - getIntTimeStamp > new TimeSpan(0, 0, ApiMinutes, 0).Ticks) { var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage(); var content = new ReqResult<string> { code = Convert.ToString((int)SatausCode.CommonCode.timeout), message = SatausCode.GetDisplayDescription(SatausCode.CommonCode.timeout), result = null }; response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json"); return; } #endregion #region 验证ApiId用户是否有权限->生成key string ApiKeyUserKey = Convert.ToString(CacheHelper.Instance.Get(String.Format(param["apiId"], "ApiKeyUserKey")));//仅获取key if (ApiKeyUserKey == null) { var ApiKeyInfo = Tbl_ApiUserManager.GetTbl_ApiUserAll().Where(x => x.ApiId == param["apiId"]).SingleOrDefault(); if (!string.IsNullOrWhiteSpace(ApiKeyInfo.ApiId)) { CacheHelper.Instance.Add(String.Format(ApiKeyInfo.ApiId, "ApiKeyUserKey"), ApiKeyInfo.ApiKey, 5); } else { var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage(); var content = new ReqResult<string> { code = Convert.ToString((int)SatausCode.CommonCode.power), message = SatausCode.GetDisplayDescription(SatausCode.CommonCode.power), result = null }; response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json"); return; } } #endregion #region 按key升序排序的待签名字符串并将所有参数加密 //按key升序排序的待签名字符串 var str = new StringBuilder(); foreach (var key in param.AllKeys.OrderBy(x => x)) { if (key.Equals("sign", StringComparison.OrdinalIgnoreCase)) { continue; } str.AppendFormat("{0}={1}&", key, HttpUtility.UrlEncode(param.Get(key.Trim()))); } str.AppendFormat("apikey={0}", ApiKeyUserKey); var calSignature = OperData.MD5Encrypt16(str.ToString()); var signature = param.Get("sign").Trim(); if (!calSignature.Equals(signature, StringComparison.OrdinalIgnoreCase)) { var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage(); var content = new ReqResult<string> { code = Convert.ToString((int)SatausCode.CommonCode.sign), message = SatausCode.GetDisplayDescription(SatausCode.CommonCode.sign), result = null }; response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json"); return; } #endregion } catch (Exception ex) { var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage(); var content = new ReqResult<string> { code = Convert.ToString((int)SatausCode.CommonCode.ex), message = SatausCode.GetDisplayDescription(SatausCode.CommonCode.ex), result = null }; response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json"); return; } base.OnActionExecuting(actionContext); } } }
我的业务 写接口的 ing 验证接口合法性 action 处理事件 ed 记录日志的 但是出现了 ing 验证 ed 不会执行的(ed里面的日志不记录了)具体是
onactionexecuting里面不让他进入action里面对么?
但是 会导致 onactionexecuted 这个记录日志的过滤器 也不执行了,解决方案:onactionexecuting 创建日志 通过状态码Guid ,然后如果经过在action就在 onactionexecuted 更新日志,否在就在不进入action也就不会进入onactionexecuted 的onactionexecuting 过滤器return false 上一行去更新。
(onactionexecuting、onactionexecuted 统简称ing和ed过滤器)
3、异常拦截器
1、创建拦截器并写入代码
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web; using System.Web.Http.Filters; using System.Web.Mvc; namespace Web.App_Start { /// <summary> /// 异常拦截/实践通过拦截视图 /// </summary> public class ExceptionFilterAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { if (filterContext.ExceptionHandled) { return; } Exception ex = filterContext.Exception; string message = $"消息类型:{ex.GetType().Name}<br />" + $"消息内容:{ex.Message}<br />" + $"引发异常的方法:{ex.TargetSite}<br />" + $"引发异常的对象:{ex.Source}<br />" + $"异常目录:{filterContext.RouteData.GetRequiredString("controller")}<br />" + $"异常方法:{filterContext.RouteData.GetRequiredString("action")}<br />" + $"错误详细记录:{ex.StackTrace}"; /*string message = string.Format("<br>消息类型:{0}<br>消息内容:{1}<br>引发异常的方法:{2}<br>引发异常的对象:{3}" , filterContext.Exception.GetType().Name , filterContext.Exception.Message , filterContext.Exception.TargetSite , filterContext.Exception.Source); string errorMessage = string.Format( "Error Message: {0}<br/>Error StackTrace: {1}", ex.message, ex.StackTrace );*/ filterContext.HttpContext.Response.Write(message); filterContext.ExceptionHandled = true; //不报告异常 } } }
2、添加全局
3、运行测试
特例:
2、系统过滤器 OutputCache过滤器
1、ActionResult结果进行缓存,概括地说,就是当你的请求参数没有发生变化时,直接从缓存中取结果,不会再走服务端的Action代码了。
[OutputCache(Duration = 300)] public ActionResult Index(int? id,string name) { return DateTime.Now.ToString(); }
如果你重复调用Index()
action(不断刷新当前页面), 那么你将看到当前的内容在Duration = 20秒内是不变的.
请求此Action的url可以为: person/Index?id=100&name="bird"。
当第一次请求这个地址时,会执行Index方法,并把结果缓存起来,且过期时间为300秒。
接下来,如果不改变id和name参数的前提下,在距离上次请求300秒内,再来请求这个地址,不会执行Index方法,直接从缓存中拿结果。
当id或者name的参数值发生变化时,发送请求时,会执行index方法,然后再缓存此结果。
[Output(Duration=300)],这种方法没有指明具体针对哪个参数来缓存,所以默认是针对所有参数,即任何一个参数值发生变化,都会缓存一份。
那么,如果,我想指定具体的参数,进行缓存该如何做呢?请看下一个方案。
2、通过对请求指定参数的ActionResult结果进行缓存
[OutputCache(Duration = 300,VaryByParam="id")]
此种方式,指明了缓存是针对哪个参数来做的,即只有当id参数值发生变化的时候,才做缓存,其他机制同第一种。
2、FlagAttribute
枚举优雅基本用法-在值String和int之间转换
//需要的方法 public string GetEnumDescription(Enum enumValue) { string str = enumValue.ToString(); System.Reflection.FieldInfo field = enumValue.GetType().GetField(str); object[] objs = field.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false); if (objs == null || objs.Length == 0) return str; System.ComponentModel.DescriptionAttribute da = (System.ComponentModel.DescriptionAttribute)objs[0]; return da.Description; } //定义枚举 enum RoleType { [Description("子公司负责人")] ZMSManager = 1, [Description("集团人力")] JTHR = 2, [Description("考核人")] AssessPerson = 3, [Description("订立人")] MakePerson = 4, [Description("系统管理员")] SysManager = 5 } //调用方法 string returnValue = GetEnumDescription((RoleType)(Enum.Parse(typeof(RoleType),"1"))); //返回值字符串:子公司负责人
Flags方式
如果对一个值可以包含多个,那么可以使用枚举,加上Flags
- 使用FlagsAttribute枚举才是对数字值执行按位运算 (AND、 OR 独占或) 的自定义属性。
- 在 2 的幂,即 1、 2、 4、 8 等中定义枚举常量。 这意味着不重叠中组合的枚举常量的各个标志。
在权限的管理中,常常会出现一个权限包含的现象。例如,有三种基本权限:职员A、职员B、职员C.在此基础上,有经理权限,它包括A和B两种权限;还有老板权限,包含A/B/C三种权限。
在代码中,我们可以用枚举来管理这些权限。
class Program { [Flags]//表示可以将枚举对象视为位标志 public enum EnumHasFlag { A = 1 << 0, B = 1 << 1, C = 1 << 2, //定义权限枚举 Manager = A | B, //通过Flags进行域计算得到的另一个枚举 Boss = A | B | C, }//权限设计如果是常量的话通常用2的幂次方,防止值重复 /* <<左移操作符, 将第一个操作数向左移动第二个操作数指定的位数,空出的位置补0。 左移相当于乘. 左移一位相当于乘2;左移两位相当于乘4;左移三位相当于乘8。 如: x<<1= x*2 x<<2= x*4 x<<3= x*8 x<<4= x*16 //至于为什么是 2/4/8?答:因为左移动计算就是将二进制的1向左边移动例如:左边二进制|右边常量 0001=1 左移则为0010=2 继续左移动 0100=4 位移计算方式常量则就是4,这是位移的计算方式。 */ static void Main(string[] args) { var rightA = EnumHasFlag.Boss; var rightB = EnumHasFlag.Manager; if (rightA.HasFlag(EnumHasFlag.C)) Console.WriteLine("rightA can do this"); if (rightB.HasFlag(EnumHasFlag.C)) Console.WriteLine("rightB can do this"); } }
与数据库管理
using System; using System.ComponentModel; namespace myApp { class Program { [Flags] public enum Permission { create = 1, read = 2, update = 4, delete = 8, } static void Main(string[] args) { Permission permission = Permission.create | Permission.read | Permission.update | Permission.delete; Console.WriteLine("1、枚举创建,并赋值……"); Console.WriteLine(permission.ToString()); Console.WriteLine((int)permission); permission = (Permission)Enum.Parse(typeof(Permission), "5"); Console.WriteLine("2、通过数字字符串转换……"); Console.WriteLine(permission.ToString()); Console.WriteLine((int)permission); permission = (Permission)Enum.Parse(typeof(Permission), "update, delete, read", true); Console.WriteLine("3、通过枚举名称字符串转换……"); Console.WriteLine(permission.ToString()); Console.WriteLine((int)permission); permission = (Permission)7; Console.WriteLine("4、直接用数字强制转换……"); Console.WriteLine(permission.ToString()); Console.WriteLine((int)permission); permission = permission & ~Permission.read; Console.WriteLine("5、去掉一个枚举项……"); Console.WriteLine(permission.ToString()); Console.WriteLine((int)permission); permission = permission|Permission.delete; Console.WriteLine("6、加上一个枚举项……"); Console.WriteLine(permission.ToString()); Console.WriteLine((int)permission); //1 3 5 7 都是在权限指定 在数据库内 /* 判断是否包含指定枚举: | 符号,把所有二进制数据进行合并,有一个或两个1都返回1 则: color1|Color.Red; 等于5|1,等于5 color1|Color.Bule; 等于5|4,等于5 color1|Color.Green; 等于5|2,等于7 color1|Color.White; 等于5|8,等于13 */ } } }
在数据库中判断:
AND (@permission IS NULL OR @permission=0 OR permission &@permission =@permission)
上面的sql语句同样可以判断多个权限
这里权限我改了 不是枚举 是int数据结构如下
/* Navicat MySQL Data Transfer Source Server : 47.94.174.85_3306 Source Server Version : 50719 Source Host : 47.94.174.85:3306 Source Database : testDb Target Server Type : MYSQL Target Server Version : 50719 File Encoding : 65001 Date: 2019-04-14 23:54:20 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for t_authorization -- ---------------------------- DROP TABLE IF EXISTS `t_authorization`; CREATE TABLE `t_authorization` ( `Id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Id自增', `Name` varchar(50) NOT NULL, `Pass` varchar(50) NOT NULL, `permission` int(11) DEFAULT NULL, PRIMARY KEY (`Id`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of t_authorization -- ---------------------------- INSERT INTO `t_authorization` VALUES ('1', '张三', '4444', '1'); INSERT INTO `t_authorization` VALUES ('2', '李四', '123456', '2'); INSERT INTO `t_authorization` VALUES ('3', '王五', '123456', '4');
执行的操作如下
set @permission=1; UPDATE t_authorization SET Name= '4444' WHERE id= '1' AND (@permission IS NULL OR @permission=0 OR permission &@permission =@permission) -- AND (@permission IS NULL OR @permission=0 OR permission &@permission =@permission) 如何数据是我上面的那样的话 这样Sql执行就相当于 验证该字段 @permission局部变量是否等于数据库permission字段,permission字段为int类型-- 权限肯定不会这样做的,存的应该是权限标识的集合 已测--
经验证 permission 我一开始写 1 2 4 是错的 不应该是数*(这数不是奇偶数而是2的立方数叠加的),应该是对应的数即权限的集合值 例如 3 5 6 7 9。 网上写法 (@permission IS NULL OR @permission=0 OR permission &@permission =@permission) 中解读
@permission IS NULL 是null 为true执行
@permission=0 是0为true执行
OR permission &@permission =@permission 按位与
比如:
SELECT 4&1 0
SELECT 4&2 0
SELECT 4&4 4
SELECT 5&4 4
SELECT 6&4 4
SELECT 7&4 4
SELECT 8&4 0
SELECT 9&4 4
SELECT 10&4 4
SELECT 8&2 0 //说明值相没有包含值9
SELECT 9&2 0 //说明值相没有包含值9
SELECT 8&2 2
SELECT 9&2 0
注意:左边&右边 即左边是权限集合,右边是权限标识 最后结果也是权限标识
SQL语句如下:
SELECT * FROM TableName WHERE ColumnName & 1=1 /*或者*/ SELECT * FROM TableName WHERE ColumnName | 1=ColumnName
或者
set @permission=1; UPDATE t_authorization SET Name= '4444' WHERE id= '1' AND (@permission IS NOT NULL OR @permission!=0 OR permission &@permission =@permission) -- @permission IS NOT NULL 不为空 @permission!=0 不为0 permission某个权限集合 @permission 某个权限 最好关联外表即权限的列表 --
首先:
1、判断权限不是靠 大于比较的 1 2 4 8 这是权限每一种展示 3 5 6 7 9 10 11 12 13 14 15 则是这几个可以拼接的合 代表一种包含的权限 通过位移 计算是哪几种权限的集合
2、靠位移(经检验位移运算符已解决, 比如4这个权限集合是从1开始 那几个数值位移得到的,然后就可以几个数值进行判断是否包含- 位移的是二进制的1 所以和1组合的对于我们二进制数)
数据库动态添加权限数据
INSERT INTO `t_authorization` VALUES ('1', '张三', '4444', '1'); INSERT INTO `t_authorization` VALUES ('2', '李四', '123456', '2'); INSERT INTO `t_authorization` VALUES ('3', '王五', '123456', '4'); 例如要动态插入这个三个后面,可能出现的情况1、位移数会越来越大。2、删除一个值,将新的插入进入 可能会导致权限 乱(仅局限与编辑操作) 解决方案: 方案一 1、Id设置自增。 2、设置权限值全部为2。 3、权限计算通过 2>>(主键-1) 即我们插入的数据格式 INSERT INTO `t_authorization` VALUES ('1', '张三', '4444', '2'); INSERT INTO `t_authorization` VALUES ('2', '李四', '123456', '2'); INSERT INTO `t_authorization` VALUES ('3', '王五', '123456', '2'); 注:这样我们的权限控制都是通过计算得出(甚至字段列2都不需要)。
方案二
INSERT INTO `t_authorization` VALUES ('1', '张三', '4444', '1'); INSERT INTO `t_authorization` VALUES ('2', '李四', '123456', '2'); INSERT INTO `t_authorization` VALUES ('3', '王五', '123456', '4'); 这种添加一个权限列 把权限值 写入类内
3、DescriptionAttribute
所谓的enum扩展 其实就是用反射动态获取给定值
4、HelpAttribute
[HelpAttribute("Information on the class MyClass")]
class MyClass
{
}
一、C#中几个简单的内置Attribute
1、Conditional:起条件编译的作用,只有满足条件,才允许编译器对它的代码进行编译。一般在程序调试的时候使用。
2、DllImport:用来标记非.NET的函数,表明该方法在一个外部的DLL中定义。
3、Obsolete:这个属性用来标记当前的方法已经被废弃,不再使用了。
C# typeof() 和 GetType()区是什么? 1、typeof(x)中的x,必须是具体的类名、类型名称等,不可以是变量名称。 2、GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法,它的作用和typeof()相同,返回Type类型的当前对象的类型。 比如有这样一个变量i: Int32 i = new Int32(); i.GetType()返回值是Int32的类型,但是无法使用typeof(i),因为i是一个变量,如果要使用typeof(),则只能:typeof(Int32),返回的同样是Int32的类型。