新年开场篇,欢迎来点赞;本篇和大家分享的是使用webapi做得接口服务验证框架,需求来源是我打算把上篇提到的图片验证码做成一种服务提供给大家,尽管我在上篇已经把代码打包开源了,但是如果有一种快速对接成功的服务,我想很多人也非常想使用吧,目前这服务已经上线并在nuget上发布有sdk客户端包(nuget包地址:Install-Package ShenNiuApi.SDK),值得庆幸的是仅上线一天就有46次的下载量(挺高兴的),兴许有感兴趣的朋友可以去nuget官网地址查看:神牛步行3的Nuget包;下面要分享的是在发布服务时候涉及到的验证调用方的架构,希望大家能够喜欢,也希望各位多多"扫码支持"和"推荐"谢谢(对了最近做了一个服装店地址:神牛衣柜3,希望需要买衣服鞋子的朋友多多捧场);
» 验证架构的需求分析 和 表结构的设计
» 使用webapi的ActionFilterAttribute做账号的统一验证
» ShenNiuApi.SDK客户端代码的分享
» 使用NuGet Package Explorer工具生成ShenNiuApi.SDK的nuget包并发布到nuget网站上
下面一步一个脚印的来分享:
» 验证架构的需求分析 和 表结构的设计
. 验证架构的需求分析
首先,对于一个接口服务来说通常会有一定的账号限制,必须要调用方使用符合规定并正确的账号传递给接口方才能调用成功接口,这里我们就以互联网行业一般接口验证的来制定我们的需求:
1. 接口验证需要:账号,密码,ip组,Token;通常密码需要加密,市面上加密方式很多如Md5,3des等加密方式;ip组顾名思义就是用来验证调用方请求的ip是否符合接口方新开账号时候绑定的指定ip,对于现今互联网行业来说分布式调用不是什么新鲜事了,所以这里需要的是ip组;Token一般都是由接口方制定的防篡改参数的保护措施,对于请求和接受双方需要有同样的Token值才能进行接口响应,通常需要有一个秘钥来促成加密;
2. 除了账号验证外有些需要接口需要按照调用次数来收取费用,因此我们这里有了次数限制的需求,但通常作为不同业务接口来说成本可能会高,尽管是同一个提供商提供的接口很有可能精细到每个接口的调用次数限制,所以我们这里设计就按照这种方式来的,真实业务具体看需求而定;
3. 通常我们对方开放接口时,会直接提供sdk客户端,方便调用方直接引用后直接可以调用方法不用再去管具体是什么请求协议啊或者调用接口方法不对等问题,这种提供sdk的方式直接只需要账号,密码,秘钥等就直接可以使用了,方便快捷而且在一定程度上避免了接口地址的暴露,可谓是好处多多;不好的第三就如同手机客户端,只要服务端更新了一些必要性的参数属性,那每次都需要调用方更新sdk包,不过这都不是事儿;
. 表结构的设计
有了上面的简要分析,咋们来看下数据库的表结构,这里主要用数据库来存储对应开通的接口账号,密码等信息和调用接口对应的次数:
表:MoApiUser(账号表) MoApiManager(接口方法表) MoApiRelation(账号与方法表关系表,其中包含有调用次数) MoLog(日志表)
表数据:
» 使用ActionFilterAttribute做账号的统一验证
有了上面需求和表的设计,下面我们来看下怎么使用Action的过滤器来统一做验证,在这之前我们需要了解下:为什么不弄一个父级的ApiController然后在里面做验证在来继承呢,而用Action过滤器来做呢,其实这两种方式都行,不过后者可以对那些不需要接口账号的接口做开放,容易控制;好了咋们一起看下Action过滤器里面着么做验证的,首先创建类 UserValidateAttribute 并继承 ActionFilterAttribute ,然后在给她一个属性ApiKeyId,这个属性用来传递调用方调用的接口编号(也就是对应接口的Id,这里对应数据库表MoApiManager中的ApiKeyId),这样做的目的是使用编号来方便数据库中做调用某个接口的统计,针对我们上面需求分析说的次数限制到某个接口上,而不是限制在账号上面(当然前者和必须和账号关联才行),因此我们定义一个这样的枚举方便对应接口中的数字:
1 /// <summary> 2 /// 接口与数据库关联编号 3 /// </summary> 4 public enum ApiKeyId 5 { 6 7 文字验证码 = 1, 8 图片验证码 = 2 9 10 }
然后在过滤器中重新 public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) ,在OnActionExecuting中做非空验证,账号密码是否匹配,Token的验证,ip组的匹配,次数的对比,因此有了如下整体的自定义过滤器的代码:
1 public class UserValidateAttribute : ActionFilterAttribute 2 { 3 4 /// <summary> 5 /// 数据库与程序关联标号 6 /// </summary> 7 public MoEnumHelper.ApiKeyId ApiKeyId { get; set; } 8 9 public UserValidateAttribute(MoEnumHelper.ApiKeyId apiKeyId) 10 { 11 this.ApiKeyId = apiKeyId; 12 } 13 14 public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) 15 { 16 17 var response = new MoShenNiuBaseResponse(); 18 var sbLog = new StringBuilder(string.Empty); 19 try 20 { 21 22 if (this.ApiKeyId <= 0) { response.Msg = "接口暂未开通,请联系管理员"; return; } 23 sbLog.AppendFormat("当前服务:{0};", this.ApiKeyId); 24 25 var request = actionContext.Request; 26 27 #region 验证 28 29 #region 非空验证 30 if (actionContext.ActionArguments.Count <= 0) { response.Msg = "请求格式不正确,请检查"; return; } 31 var moRequest = actionContext.ActionArguments["request"] as MoShenNiuBaseRequest; 32 33 dynamic httpContext = actionContext.Request.Properties["MS_HttpContext"]; 34 var userIp = httpContext.Request.UserHostAddress; 35 sbLog.AppendFormat("UserName:{0};Ip:{1};Token:{2};UserPwd:{3};", moRequest.UserName, userIp, moRequest.Token, moRequest.UserPwd.ToUpper()); 36 if (string.IsNullOrWhiteSpace(moRequest.UserName) || string.IsNullOrWhiteSpace(moRequest.UserPwd)) 37 { 38 response.Msg = "接口账号或密码不能为空"; 39 return; 40 } 41 else if (string.IsNullOrWhiteSpace(moRequest.Token)) 42 { 43 response.Msg = "Token不能为空"; 44 return; 45 } 46 #endregion 47 48 using (StageEntities db = new StageEntities()) 49 { 50 51 #region 验证账号是否存在 52 53 var userInfo = db.MoApiUsers.Where(b => b.UserName.ToUpper() == moRequest.UserName.ToUpper() && b.UserPwd.ToUpper() == moRequest.UserPwd.ToUpper()).SingleOrDefault(); 54 if (userInfo == null) 55 { 56 response.Msg = "接口账号或密码错误"; 57 return; 58 } 59 #endregion 60 61 #region 获取对应账号秘钥,验证token 62 63 var newToken = Md5Extend.GetMd5Hash(string.Format("{0}_{1}_{2}", 64 moRequest.UserName.ToUpper(), 65 userInfo.TokenKey.ToUpper(), 66 moRequest.UserPwd.ToUpper())); 67 68 sbLog.AppendFormat("服务器TokenKey:{0};Token:{1};", userInfo.TokenKey.ToUpper(), newToken); 69 70 if (!moRequest.Token.Equals(newToken, StringComparison.OrdinalIgnoreCase)) 71 { 72 response.Msg = "Token验证失败"; 73 return; 74 } 75 #endregion 76 77 #region 账号可用性 78 if (userInfo.Status != (int)MoEnumHelper.EmStatus.启用) 79 { 80 response.Msg = "接口账号暂被停用"; 81 return; 82 } 83 else if (!string.IsNullOrWhiteSpace(userInfo.Ips)) 84 { 85 if (!userInfo.Ips.Contains(userIp)) 86 { 87 response.Msg = "接口ip无效"; 88 return; 89 } 90 } 91 92 var realationInfo = userInfo.MoApiRelations.Where(b => b.MoApiManager.ApiKeyId == (int)ApiKeyId && b.MoApiManager.Status == (int)MoEnumHelper.EmStatus.启用).SingleOrDefault(); 93 if (realationInfo == null) 94 { 95 response.Msg = "接口暂未启用,请稍后重试"; 96 return; 97 } 98 else if (realationInfo.MaxNum <= realationInfo.NowNum) 99 { 100 response.Msg = "接口调用次数已满,请联系管理员"; 101 return; 102 } 103 #endregion 104 105 //通过验证 106 response.Status = (int)MoEnumHelper.EmStatus.启用; 107 108 #region 用户通过验证就增加次数 109 110 realationInfo.NowNum++; 111 var result = db.SaveChanges(); 112 sbLog.AppendFormat("最大次数:{0};当前次数:{1};增加次数:{2};", realationInfo.MaxNum, realationInfo.NowNum, result); 113 #endregion 114 } 115 116 #endregion 117 118 119 } 120 catch (Exception ex) 121 { 122 response.Msg = "500接口处理请求异常,请稍后重试"; 123 sbLog.AppendFormat("异常信息:{0};", ex.Message); 124 } 125 finally 126 { 127 128 sbLog.AppendFormat("返回Status:{0};Msg:{1};", response.Status, response.Msg); 129 //记录日志 130 StageClass._WrigLog(sbLog.ToString()); 131 132 //验证失败返回 133 if (response.Status != (int)MoEnumHelper.EmStatus.启用 || !string.IsNullOrWhiteSpace(response.Msg)) 134 { 135 actionContext.Response = new HttpResponseMessage() 136 { 137 138 StatusCode = System.Net.HttpStatusCode.OK, 139 Content = new StringContent(JsonConvert.SerializeObject(response)) 140 }; 141 } 142 } 143 } 144 }
代码中已经有了验证步骤模块的说明各位可以读一下,需要注意的地方是在Action过滤器中获取post传递给接口的对象参数方式是: var moRequest = actionContext.ActionArguments["request"] as MoShenNiuBaseRequest; 这样就能直接获取客户端传递过来的对象数据了;然后咋们再Controller代码中使用定义的过滤器,主要是在Action方法上方增加 [UserValidate(MoEnumHelper.ApiKeyId.文字验证码)]标记,通过MoEnumHelper.ApiKeyId.文字验证码来传递调用方调用的接口,这里我封装的接口是:文字验证码和图片验证码的接口,至于生成验证码的代码已经在上一篇MVC伪一个12306图片验证码已经开源出来了这里不多说,直接上整个webapi接口的Controller代码:
1 /// <summary> 2 /// ShenNiuApi - 接口 3 /// </summary> 4 [RoutePrefix("shenniuapi")] 5 public class ShenNiuController : ApiController 6 { 7 8 /// <summary> 9 /// 文字验证码 10 /// </summary> 11 /// <param name="request">验证码Request请求</param> 12 /// <returns>文字验证码图片流</returns> 13 [Route("WenZiValidateCode")] 14 [HttpPost] 15 [UserValidate(MoEnumHelper.ApiKeyId.文字验证码)] 16 public MoValidateCodeResponse GetWenZiValidateCode(MoValidateCodeRequest request) 17 { 18 19 var response = new MoValidateCodeResponse(); 20 21 try 22 { 23 //返回的验证码文字 24 var code = string.Empty; 25 //图片流 26 response.CodeStream = ValidateCode.GetValidateCodeStream(ref code); 27 if (string.IsNullOrWhiteSpace(code) || response.CodeStream.Length <= 0) { response.Msg = "获取验证码失败,请稍后重试"; return response; } 28 29 response.Code = code; 30 response.Status = (int)MoEnumHelper.EmApiStatus.成功; 31 } 32 catch (Exception ex) 33 { 34 response.Msg = "获取验证码失败,请稍后重试"; 35 } 36 37 return response; 38 } 39 40 /// <summary> 41 /// 图片验证码 42 /// </summary> 43 /// <param name="request">验证码Request请求</param> 44 /// <returns>图片验证码图片流及待验证图片坐标</returns> 45 [Route("TuPianValidateCode")] 46 [HttpPost] 47 [UserValidate(MoEnumHelper.ApiKeyId.图片验证码)] 48 public MoValidateCodeResponse GetTuPianValidateCode(MoValidateCodeRequest request) 49 { 50 51 var response = new MoValidateCodeResponse(); 52 53 try 54 { 55 //获取图片类型 56 var validateCode = ValidateCode.GetInitImgCode(); 57 if (validateCode == null || string.IsNullOrWhiteSpace(validateCode.IndexType)) { response.Msg = "获取验证码失败,请稍后重试"; return response; } 58 59 //生成图片 60 var imgCode = new List<Stage.Com.Extend.MoImgCode>(); 61 response.CodeStream = ValidateCode.CreateImgValidateStream(validateCode.IndexType, ref imgCode, strLen: 8); 62 if (imgCode.Count <= 0 || response.CodeStream.Length <= 0 || imgCode.Count <= 0) { response.Msg = "获取验证码失败,请稍后重试"; return response; } 63 64 //得到待匹配验证码坐标 65 foreach (var item in imgCode.Where(b=>b.IsChoice)) 66 { 67 response.ImgCode.Add(new Stage.Model.MoImgCode() 68 { 69 ImgUrl = item.ImgUrl, 70 Index = item.Index, 71 IndexType = item.IndexType, 72 IsChoice = item.IsChoice, 73 Point_A = item.Point_A, 74 Point_B = item.Point_B 75 }); 76 } 77 response.Code = validateCode.IndexType; 78 response.Status = (int)MoEnumHelper.EmApiStatus.成功; 79 } 80 catch (Exception ex) 81 { 82 response.Msg = "获取验证码失败,请稍后重试"; 83 } 84 85 return response; 86 } 87 88 }
感觉是不是挺简单的,其实关键点在于过滤器中验证的步骤,要明白作为一个接口需要验证哪些东西这样才能保障接口的安全;
» ShenNiuApi.SDK客户端代码的分享
我们需求分析的时候也说了通过SDK的nuget方式提供给调用方使用,方便快捷,有一定的安全性;下面我们先来看下客户端所使用到的实体类:
1 namespace ShenNiuApi.SDK 2 { 3 4 /// <summary> 5 /// 神牛步行3枚举 6 /// </summary> 7 public class MoEnumHelper 8 { 9 10 public enum EmStatus 11 { 12 禁用 = 0, 13 启用 = 1 14 } 15 } 16 17 /// <summary> 18 /// 接口验证基类 19 /// </summary> 20 public class MoShenNiuBaseRequest 21 { 22 /// <summary> 23 /// 账号 24 /// </summary> 25 public string UserName { get; set; } 26 27 /// <summary> 28 /// 密码 29 /// </summary> 30 public string UserPwd { get; set; } 31 32 /// <summary> 33 /// 加密Token(方式:Md5(账号_秘钥_密码)) 34 /// </summary> 35 public string Token { get; set; } 36 37 } 38 39 /// <summary> 40 /// 神牛接口返回基类 41 /// </summary> 42 public class MoShenNiuBaseResponse 43 { 44 /// <summary> 45 /// 返回状态: 0:失败 1:成功 46 /// </summary> 47 public int Status { get; set; } 48 49 /// <summary> 50 /// 错误信息 51 /// </summary> 52 public string Msg { get; set; } 53 } 54 55 /// <summary> 56 /// 验证码请求类 57 /// </summary> 58 public class MoValidateCodeRequest : MoShenNiuBaseRequest { } 59 60 /// <summary> 61 /// 验证码返回类 62 /// </summary> 63 public class MoValidateCodeResponse : MoShenNiuBaseResponse 64 { 65 66 public MoValidateCodeResponse() 67 { 68 this.ImgCode = new List<MoImgCode>(); 69 } 70 71 /// <summary> 72 /// 验证码类型 73 /// </summary> 74 public string Code { get; set; } 75 76 /// <summary> 77 /// 验证码图片流 78 /// </summary> 79 public byte[] CodeStream { get; set; } 80 81 /// <summary> 82 /// 图片验证坐标 83 /// </summary> 84 public List<MoImgCode> ImgCode; 85 } 86 87 /// <summary> 88 /// 图片验证码坐标 89 /// </summary> 90 public class MoImgCode 91 { 92 public string Index { get; set; } 93 94 public string IndexType { get; set; } 95 96 public string ImgUrl { get; set; } 97 98 public Point Point_A { get; set; } 99 100 public Point Point_B { get; set; } 101 102 public bool IsChoice { get; set; } 103 } 104 105 }
其中包含了账号实体,验证码请求实体和返回实体,挺简单的,因为这里接口目前只有两个嘛,当以后我不断开放接口后相信客户端的实体代码远不止于此;再来看方法,作为一个客户端肯定会有登陆账号,密码等传入的地方,我这里不是以配置文件传入的方式获取,而是直接通过客户端类构造函数来传递(构造函数上有默认参数是方便各位朋友使用接口的账号,如果有希望单独开放账号的合作伙伴可以联系我);客户端类中利用泛型T构造了一个统一的账号非空验证方法Validate和密码及Token构造的GetBaseRequest方法,对已代码:
1 /// <summary> 2 /// 得到基础请求格式 3 /// </summary> 4 /// <returns></returns> 5 private T GetBaseRequest<T>() where T : MoShenNiuBaseRequest, new() 6 { 7 var baseRequest = new T(); 8 9 baseRequest.UserName = this.UserName; 10 baseRequest.UserPwd = Md5Extend.GetMd5Hash(this.UserPwd); 11 baseRequest.Token = Md5Extend.GetMd5Hash(string.Format("{0}_{1}_{2}", 12 this.UserName.ToUpper(), 13 this.TokenKey.ToUpper(), 14 baseRequest.UserPwd.ToUpper())); 15 16 return baseRequest; 17 } 18 19 /// <summary> 20 /// 非空验证 21 /// </summary> 22 /// <typeparam name="T"></typeparam> 23 /// <param name="t"></param> 24 public void Validate<T>(T t) 25 where T : MoValidateCodeResponse 26 { 27 if (string.IsNullOrWhiteSpace(this.UserName) || 28 string.IsNullOrWhiteSpace(this.UserPwd)) { t.Msg = "账号或密码不能为空"; } 29 else if (string.IsNullOrWhiteSpace(this.TokenKey)) 30 { 31 t.Msg = "秘钥不能为空"; 32 } 33 }
好了下面直接贴出对应的客户端中调用文字验证码和图片验证码的代码:
1 namespace ShenNiuApi.SDK 2 { 3 public class ShenNiuApiClient 4 { 5 #region 属性 6 7 public string ApiUrl { get; set; } 8 9 /// <summary> 10 /// 账号 11 /// </summary> 12 public string UserName { get; set; } 13 14 /// <summary> 15 /// 密码 16 /// </summary> 17 public string UserPwd { get; set; } 18 19 /// <summary> 20 /// 秘钥 21 /// </summary> 22 public string TokenKey { get; set; } 23 #endregion 24 25 public ShenNiuApiClient() { } 26 public ShenNiuApiClient(string userName = "神牛步行3", string userPwd = "123123", string tokenKey="代码改变世界,需求决定一切", string apiUrl = "http://www.lovexins.com:1001/shenniuapi") 27 { 28 29 this.UserName = userName; 30 this.UserPwd = userPwd; 31 this.TokenKey = tokenKey; 32 this.ApiUrl = apiUrl; 33 } 34 35 /// <summary> 36 /// 得到基础请求格式 37 /// </summary> 38 /// <returns></returns> 39 private T GetBaseRequest<T>() where T : MoShenNiuBaseRequest, new() 40 { 41 var baseRequest = new T(); 42 43 baseRequest.UserName = this.UserName; 44 baseRequest.UserPwd = Md5Extend.GetMd5Hash(this.UserPwd); 45 baseRequest.Token = Md5Extend.GetMd5Hash(string.Format("{0}_{1}_{2}", 46 this.UserName.ToUpper(), 47 this.TokenKey.ToUpper(), 48 baseRequest.UserPwd.ToUpper())); 49 50 return baseRequest; 51 } 52 53 /// <summary> 54 /// 非空验证 55 /// </summary> 56 /// <typeparam name="T"></typeparam> 57 /// <param name="t"></param> 58 public void Validate<T>(T t) 59 where T : MoValidateCodeResponse 60 { 61 if (string.IsNullOrWhiteSpace(this.UserName) || 62 string.IsNullOrWhiteSpace(this.UserPwd)) { t.Msg = "账号或密码不能为空"; } 63 else if (string.IsNullOrWhiteSpace(this.TokenKey)) 64 { 65 t.Msg = "秘钥不能为空"; 66 } 67 } 68 69 /// <summary> 70 /// 获取文字验证码 71 /// </summary> 72 /// <returns></returns> 73 public async Task<MoValidateCodeResponse> GetWenZiValidateCodeAsync() 74 { 75 76 var response = new MoValidateCodeResponse(); 77 try 78 { 79 //非空验证 80 Validate(response); 81 if (!string.IsNullOrWhiteSpace(response.Msg)) { return response; } 82 83 //获取基础请求设置 84 var request = this.GetBaseRequest<MoValidateCodeRequest>(); 85 86 //json化 87 var requestStr = JsonConvert.SerializeObject(request); 88 //发送请求 89 var returnStr = string.Empty; 90 using (HttpClient client = new HttpClient()) 91 { 92 client.Timeout = TimeSpan.FromSeconds(60); 93 var stringContent = new StringContent(requestStr, Encoding.UTF8, "application/x-www-form-urlencoded"); 94 stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); 95 96 var httpResponseMessage = client.PostAsync(this.ApiUrl + "/WenZiValidateCode", stringContent).Result; 97 var stream = await httpResponseMessage.Content.ReadAsStreamAsync(); 98 using (StreamReader reader = new StreamReader(stream)) 99 { 100 returnStr = await reader.ReadToEndAsync(); 101 } 102 } 103 if (string.IsNullOrWhiteSpace(returnStr)) 104 { 105 106 return response; 107 } 108 //解析 109 response = JsonConvert.DeserializeObject<MoValidateCodeResponse>(returnStr); 110 } 111 catch (Exception ex) 112 { 113 response.Msg = ex.Message; 114 } 115 return response; 116 } 117 118 119 /// <summary> 120 /// 获取图片验证码 121 /// </summary> 122 /// <returns></returns> 123 public async Task<MoValidateCodeResponse> GetTuPianValidateCodeAsync() 124 { 125 126 var response = new MoValidateCodeResponse(); 127 try 128 { 129 //非空验证 130 Validate(response); 131 if (!string.IsNullOrWhiteSpace(response.Msg)) { return response; } 132 133 //获取基础请求设置 134 var request = this.GetBaseRequest<MoValidateCodeRequest>(); 135 136 //json化 137 var requestStr = JsonConvert.SerializeObject(request); 138 //发送请求 139 var returnStr = string.Empty; 140 using (HttpClient client = new HttpClient()) 141 { 142 client.Timeout = TimeSpan.FromSeconds(60); 143 var stringContent = new StringContent(requestStr, Encoding.UTF8, "application/x-www-form-urlencoded"); 144 stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); 145 146 var httpResponseMessage = client.PostAsync(this.ApiUrl + "/TuPianValidateCode", stringContent).Result; 147 var stream = await httpResponseMessage.Content.ReadAsStreamAsync(); 148 using (StreamReader reader = new StreamReader(stream)) 149 { 150 returnStr = await reader.ReadToEndAsync(); 151 } 152 } 153 if (string.IsNullOrWhiteSpace(returnStr)) 154 { 155 156 return response; 157 } 158 //解析 159 response = JsonConvert.DeserializeObject<MoValidateCodeResponse>(returnStr); 160 } 161 catch (Exception ex) 162 { 163 response.Msg = ex.Message; 164 } 165 return response; 166 } 167 168 } 169 }
» 使用NuGet Package Explorer工具生成ShenNiuApi.SDK的nuget包并发布到nuget网站上
因为我们要发布Sdk的nuget包,所以需要打包才行,这里就不用nuget命令了而是使用NuGet Package Explorer工具来打包,首先安装好工具后,我们打开有这样的画面:
我们选中如上图所示的选项,然后选中"编辑选项":
然后录入如下信息即可,您也可以录入更多的依赖等选项的信息:
这些信息是我发布的sdk,您们自己的可以不用填写这么详细哈哈,记得填完后再点击刚才位置的绿色钩钩(保存),好了接下来重点是在工具的右边的"Package Contents"区域=》右键鼠标=》选中如下图所示选项:
这里选项的意思添加类库dll,点击完后会有一个"Lib"出现,再右键Lib选中"Add .Net Folder"=》"V4.5"后面这个4.5是您打包项目的版本,我的是适用于4.5的,如下图:
然后再直接把您项目bin下面生成的dll(我这里是ShenNiuApi.SDK.dll)直接拖到"Package Contents"区域并且是在"V4.5"的下级,效果如下:
最后一步"保存"我们的nuget文件,先点击工具左上角的"File"=》"Save",有提示包文件保存在哪里,确定后就可以在您保存的文件夹下面看到您的nuget包了:
怎么样使用工具是不是很简单,您成功生成自己的nuget包了么;如果您想包您的包发布到nuget共全世界.net程序猿使用那么还是使用此工具“File”=》“publish”=》如图:
不错意外您的nuget包就发布上去了,并且在vs中通过搜索包也能看到,例如我这里的包:
如果能收到您自己的nuget包是不是感觉很兴奋呢哈哈,我也是当截图此刻发布两天的sdk已经有51次下载量了,不错啦;网站上的描述:
到此本章的内容就结束了,希望给您带来了学习的帮助,如果感谢我的话并且考虑要买双鞋子和衣服,不妨来小弟衣服店看看:神牛衣柜3,非常感谢您的支持,也感谢多多点赞。
代码资源包:使用ShenNiuApi.SDK的nuget包