C# 物联网智能模块管理端开发接口API实例源码分享
本帖最后由 X5ZJ 于 2020-8-14 10:21 编辑近段时间一直研究8266,功能基本实现完毕,开始考虑如何验证设备,管理设备,故开始通过结合数据库进行设备识别认证(前提自建服务器,供物联网模块运行)本帖子仅提供学习思路,有需要的可以参考学习下。大神路过不喜勿喷,违规联系删
一篇文章很长。刚才排版错乱了(浏览器一直卡顿),还要重新编辑,部分文字省略,代码里基本都注释了。不懂的可以留言交流!!
开发目的:
在DIY项目过程中,对于一些小伙伴会进行二次开发,提供一系列的处理方法,故制作本系列教程,以便更好分享,为更加方便小伙伴们的学习。
准备:vs2008/vs2010/vs2013/vs2015/vs2017等等均可。根据项目复杂度和系统配置适合自己的开发环境,本作者选择的是VS2008+.Net framework3.0
步骤:
1)环境安装省略,网上很多。
2)打开VS2008
3)文件》新建》文件》选择“一般处理程序”,起个名字,例如“WebSite_9ibox”
tip:根据您掌握的程度,可以选择不同的文件类型,因为不需要界面,仅提供接口处理,故教程仅提供一般处理程序即可实现,调用方式参考
http://URL路径:端口号/文件路径/文件名称.ashx?action=参数1&token=参数2
常见的接口内容
1)登录身份验证(合法、不合法)返回一些有用的参数
2)合法的身份下的设备列表及设备状态(激活的设备列表)
3)设备是否有效或是否激活
4)设备激活(拟计划扫二维码的形式进行激活 ajax进行交互提醒,设备只能激活一次)
5)扩展:修改服务端设备信息(名称等)、充值(可以使用一些扩展业务:邮件通知,语音电话通知,个性定制的内容等)
6)计划扩展:直接对设备进行高级配置(后续完善,对设备进行写操作,通过web界面对设备参数配置并写入或通过桌面winform管理软件进行配置)
调试工具可以用浏览器或postman进行调试
C# 物联网开发API接口系列(二)
【提纲】
1、如何写验证登录(参数:账号,密码)
2、switch方法的使用
3、json格式的使用方法,一些函数的使用注意事项
<%@ WebHandler Language="C#" Class="check" %>
/*******************************************
* 功能:管理客户端验证其登录合法性 2020.8.13
* 参数:
* action[必须]关键词决定查询类型
* 根据自定义方法 传递参数 查询结果反馈打印
* 举例 iot.jiuai.ltd?action=check&token=身份证&其他参数体
*******************************************/
using System;
using System.Text;
using System.Web;
using System.Data;
using System.Web.Services; //WebMethod 依赖于此引用
using DatabaseHelper; //数据库 增删改查 的方法 配置等
using System.Data.SqlClient; //sql命名空间
using md5Helper; //md5加密 验证密码时可用到
第一行:声明语言及类的名称(文件名称)
第二步:引用命名空间(需要多少引用多少,且第三方的要应用组件或部件)或自写的方法类等
调用接口的方法:url 方式之 get的方法
public class check : IHttpHandler
{
//一些公共的变量或必须的参数 在此类最上层申请,多个方法可以共享调用
}
public void ProcessRequest (HttpContext context)
{
//所有参数处理入口函数(如果多个方法 通过不同的参数进行 分支处理)
//此处用的是 switch 语句;下面举例说明
}
public void ProcessRequest (HttpContext context)
{
context.Response.ContentType = "text/plain";//参数类型
//必须参数 在类的顶层申请变量
act = context.Request["action"]; //【必须】 参数名称可以自己定义
token = context.Request["token"];//【必须】访问时携带,登录时查询更新
string regpacket=""; //注册包(设备标识码)
try
{
//********连接数据库***************
if (!SqlHelper.isConnected)
SqlHelper.check_SqlConn();
//**********************************
//本教程 对数据库进行操作 所以必须连接数据库(自定义类方法)
//针对不同类型机型不同分支处理使用switch语句
switch (act)
{//目的分类型查询,名称自定义(这里分支名称参数和方法名称一致便于查询调用)
case "login"://通过账号密码获取身份证token
string uc = context.Request["uc"]; //此方法需要2个参数 账号和密码
string pwd = context.Request["upwd"]; //密码
context.Response.Write(login(uc,pwd));//查询并返回结果json格式
break;
case "getDevList"://获取该token下拥有的设备列表
uid = getUidbyToken(token);
context.Response.Write(getDevList(token));
break;
case "getUserInfo"://通过token获取身份信息
uid=getUidbyToken(token);
context.Response.Write(ujson);
break;
case "checkDevValidity"://检查设备是否有效
uid = getUidbyToken(token);
regpacket = context.Request["regpacket"];
context.Response.Write(checkDevValidity(regpacket));
break;
case "actionBox"://激活设备(针对未激活的设备)
uid = getUidbyToken(token);
regpacket = context.Request["regpacket"];
context.Response.Write(actionBox(regpacket));//针对超级管理员可以做各种设备激活
break;
default://所有未定义的方法,一律返回当前时间
context.Response.Write(DateTime.Now);
break;
}
}
catch (Exception ex)
{//返回异常/错误描述
context.Response.Write("Error:" + ex.Message.ToString());
}
}
登录验证 (有些解释文字就不写了看代码)
/// <summary>
/// 获取用户token,通过账号密码
/// </summary>
/// <param name="uc">账号或手机号</param>
/// <param name="pwd">密码(未加密,明文)</param>
/// <returns></returns>
private string login(string uc, string pwd)
{
StringBuilder sb = new StringBuilder();//字符串拼接用到
string result = "";
if (SqlHelper.isConnected)
{//数据库已连接
try
{
SqlConnection SqlConn = new SqlConnection(SqlHelper.ConnString);
if (SqlConn.State != ConnectionState.Connecting)
{
SqlConn.Open();// 打开数据库连接
}
//修改加密方法根据自己需要 可以加密后与数据库进行对比
SqlCommand sqlCmd = new SqlCommand("SELECT top 1 uid,fuid,uCount,uToken,uTel,uName,uPwd FROM user_List WHERE (uTel='" + uc + "' OR uCount='" + uc + "') AND uPwd='" + md5Helper.MD5Util.MD5Hash(pwd,32,true) + "' AND uStatus=1", SqlConn);
SqlDataReader rd = sqlCmd.ExecuteReader();//返回记录集
if (rd.HasRows)//HasRows判断reader中是否有数据
{
if (rd.Read())
{//读取1条数据,如果多条时 用while进行操作
uid = Convert.ToInt16(rd["uid"]); //返回用户id
fuid = Convert.ToInt16(rd["fuid"]);//模块父id (可无限分级)顶级为0,子级时为uid
utoken = rd["uToken"].ToString();//令牌(***)
utel = rd["uTel"].ToString(); //绑定的电话
uname = rd["uName"].ToString();//设备昵称(暂不支持直接汉字,考虑编码方式)
//返回 用户信息(公共变量)
ujson = "{\"uid\":" + uid + ",\"fuid\":" + fuid + ",\"utoken\":\"" + utoken + "\",\"uname\":\"" + uname + "\",\"utel\":\"" + utel + "\"}";
}
//json标准格式除数字外,所有key和value必须有引号,此c#里用\"转移,注意{}的使用和逗号的处理!
//在这拼格式 非数据引用的{}不能出现Format里面否则无法识别;Format里只能数字对应的数据
result = string.Format("\"data\":\"{0}\",\"code\":{1},\"time\":\"{2}\"", utoken, 200, svrNowTime());//全部数据的最后一个逗号处理
}
else
{//无结果时
result = string.Format("\"data\":\"{0}\",\"code\":{1},\"time\":\"{2}\"", "验证失败,请检查账号或密码!", 0, svrNowTime());
}
rd.Close();//统一释放资源
rd = null;
sqlCmd = null;
SqlConn.Close();//关闭数据库连接
SqlConn = null;//否则不更新原内容
}
catch (Exception ex)
{//错误时(字符串为空也必须加getNewString处理)
result = string.Format("\"data\":\":{0}\",\"code\":{1},\"time\":\"{2}\"", ex.Message.ToString(), -1, svrNowTime());
}
}
return "{" + result + "}";//在此组合json格式;如果有全角的{} 时替换成半角的。最后处理!
}
//code:为数字类型,data和time为字符串类型 注意加双引号
//***重点理解**** json中的{}和format中{0},{1}等参数的调用不能在一个字符串中一次成型
//json的开头{和结尾}不要出现在 fromat中,在外面组合或全角的符号替代最后再替换掉!!
/// <summary>
/// 获取系统时间
/// </summary>
/// <returns></returns>
private string svrNowTime()
{
return string.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now);
}
/// <summary>
/// Json中不规则字符替换
/// </summary>
/// <param name="sstr"></param>
/// <returns></returns>
private string jsonReplace(string sstr)
{
return sstr.Replace("{", "{").Replace("}", "}");//前2个是中文的{}后面2个是英文的{},数据的最后个,的处理
}
//通用方法(DELETE UPDATE)
/// <summary>
/// 执行SQL(执行UPDATE,DELETE,INSERT 操作)一条记录 ExecSqlHelper.execSQL
/// </summary>
/// <param name="exesql">sql语句</param>
/// <returns></returns>
public static string execSQL(string exesql)
{
//ConfigSQL();//配置连接参数
//----------------------------------
string execRet = "";
SqlConnection SqlConn = new SqlConnection(SqlHelper.ConnString);
try
{
if (SqlConn.State == ConnectionState.Closed)
{
SqlConn.Open();
}
SqlCommand sqlCmd = new SqlCommand(exesql, SqlConn);
int resultNum = sqlCmd.ExecuteNonQuery();//影响结果的行数
if (resultNum > 0)
{
execRet = "OK:" + resultNum;//成功
}
sqlCmd = null;
SqlConn.Close();
SqlConn.Dispose();
}
catch (Exception ex)
{
execRet = "SORRY:" + ex.Message.ToString();//失败
}
return execRet;
}
//激活设备
/// <summary>
/// 激活设备
/// </summary>
/// <param name="regpacket">注册包</param>
/// <returns></returns>
private string actionBox(string regpacket)
{
string exeResult = "";
try
{
SqlConnection SqlConn = new SqlConnection(SqlHelper.ConnString);
if (SqlConn.State != ConnectionState.Connecting) SqlConn.Open();
if (admintoken == token)
{//是管理员(可以激活列黑的启用) 条件不同
exeResult = ExecSqlHelper.execSQL("UPDATE box_List SET bActiveDate=GETDATE(),bStatus=1 WHERE bStatus!=1 AND bRegpacket='" + regpacket + "'");
}else{//扫码激活
exeResult = ExecSqlHelper.execSQL("UPDATE box_List SET bActiveDate=GETDATE(),bStatus=1 WHERE bStatus=0 AND bRegpacket='" + regpacket + "'");
}
//通用方法返回值
if (exeResult.IndexOf("OK") == 0)
{
exeResult = string.Format("\"data\":\"{0}\",\"code\":{1},\"time\":\"{2}\"","设备激活成功!", 200, DateTime.Now);
}
else
{
exeResult = string.Format("\"data\":\"{0}\",\"code\":{1},\"time\":\"{2}\"", "设备激活失败,请检查参数是否是否正确或已激活!", 0, DateTime.Now);
}
}
catch (Exception ex)
{
exeResult = string.Format("\"data\":\":{0}\",\"code\":{1},\"time\":\"{2}\"", ex.Message.ToString(), -1, DateTime.Now);
}
return "{" + exeResult + "}";
}
/// <summary>
/// 获取设备列表
/// </summary>
/// <param name="_token">令牌</param>
/// <returns></returns>
private string getDevList(string _token)
{
string result = "";
int listnum = 0;
StringBuilder sb = new StringBuilder();
try
{
if (SqlHelper.isConnected)
{
SqlConnection SqlConn = new SqlConnection(SqlHelper.ConnString);
if (SqlConn.State != ConnectionState.Connecting)
{
SqlConn.Open();// 打开数据库连接
}
//注意:参数是token查询用的是 uid 之前有一方法已经调用了 uid = getUidbyToken(token);
SqlCommand sqlCmd = new SqlCommand("SELECT * FROM box_List WHERE bForUid=" + uid, SqlConn);
SqlDataReader rd = sqlCmd.ExecuteReader();//返回的是记录集
if (rd.HasRows)
{
while (rd.Read())
{
listnum++;
sb.AppendFormat("{" + string.Format("\"regpacket\":\"{0}\",\"boxname\":\"{1}\",\"actived\":{2}", rd["bRegpacket"].ToString(), rd["bName"].ToString(),Convert.ToInt16(rd["bStatus"])) + "}");//可继续扩展其他属性
sb.AppendFormat(",");//最后一条需要处理此符号
}
sb.Remove(sb.ToString().LastIndexOf(','), 1);//处理最后一位符号(如何处理最后一个逗号)
}
rd.Close();//统一释放资源
rd = null;
sqlCmd = null;
SqlConn.Close();//关闭数据库连接
SqlConn = null;//否则不更新原内容
}
//当data为数组时加[] 非数组及json格式写法
if(listnum>0)
result = string.Format("\"data\":[{0}],\"code\":{1},\"time\":\"{2}\"", sb.ToString().Replace("{", "{").Replace("}", "}"), 200, svrNowTime());
else
result = string.Format("\"data\":\"{0}\",\"code\":{1},\"time\":\"{2}\"", "请检查参数的合法性:" + _token, 0, svrNowTime());
}
catch (Exception ex)
{
result = string.Format("\"data\":\":{0}\",\"code\":{1},\"time\":\"{2}\"", ex.Message.ToString(), -1, DateTime.Now);
}
return "{"+result+"}";//组合成标准的json格式的头尾
}
/// <summary>
/// 验证智能模块的合法性
/// </summary>
/// <param name="reg">注册包</param>
/// <returns></returns>
private string checkDevValidity(string reg)
{
string _retu = "";
try
{
SqlConnection SqlConn = new SqlConnection(SqlHelper.ConnString);
if (SqlConn.State != ConnectionState.Connecting)
{
SqlConn.Open();// 打开数据库连接
}
//bStatus 1启用0禁用-1列黑
SqlCommand sqlCmd = new SqlCommand("SELECT * FROMbox_List WHERE bStatus=1 AND bForUid=" + uid + " AND bRegpacket='" + reg + "'", SqlConn);
SqlDataReader rd = sqlCmd.ExecuteReader();
if (rd.HasRows)
{
rd.Read();
_retu = string.Format("\"data\":\"{0}\",\"code\":{1},\"time\":\"{2}\"", rd["bRegpacket"], 200, DateTime.Now);
}
else
{
_retu = string.Format("\"data\":\"{0}\",\"code\":{1},\"time\":\"{2}\"", "验证失败,请检查参数是否正确!", 0, DateTime.Now);
}
}
catch (Exception ex)
{
_retu = string.Format("\"data\":\":{0}\",\"code\":{1},\"time\":\"{2}\"", ex.Message.ToString(), -1, DateTime.Now);
}
return "{" + _retu + "}"; //重新组合标准json格式头尾{}
}
//通过token获取uid
private int getUidbyToken(string tok)
{
int _uid;
try
{
SqlConnection SqlConn = new SqlConnection(SqlHelper.ConnString);
if (SqlConn.State != ConnectionState.Connecting)
{
SqlConn.Open();// 打开数据库连接
}
SqlCommand sqlCmd = new SqlCommand("SELECT top 1 uid,fuid,uToken,uTel,uName FROM user_List WHERE uToken='" + tok + "' AND uStatus=1", SqlConn);//uRole=8
SqlDataReader rd = sqlCmd.ExecuteReader();//返回的是记录集 AND uStatus=1
if (rd.HasRows)
{
if (rd.Read())
{
uid = Convert.ToInt16(rd["uid"]);//***主要查询这个
fuid = Convert.ToInt16(rd["fuid"]);
utoken = rd["uToken"].ToString();
utel = rd["uTel"].ToString();
uname = rd["uName"].ToString();
//
ujson = "{\"uid\":" + uid + ",\"fuid\":" + fuid + ",\"utoken\":\"" + utoken + "\",\"uname\":\"" + uname + "\",\"utel\":\"" + utel + "\"}";
}
_uid = uid;//返回这个
}
else
{
_uid = -1;//没找到
}
}
catch (Exception ex)
{
_uid = 0;//错误异常时0
}
return _uid;
}
https://p3-tt.byteimg.com/large/pgc-image/4ec62308cf2943e99bad2c75dcd8dc56?from=pc
合法验证通过返回注册包格式 条件注册包
https://p1-tt.byteimg.com/large/pgc-image/34721284af1b44a7998d0203e6e8fd8f?from=pc获取设备列表(条件:令牌)
https://p1-tt.byteimg.com/large/pgc-image/d5813351033845bc8f1521a1a08c158a?from=pc成功获取令牌
https://p1-tt.byteimg.com/large/pgc-image/afae10cb7016460ab193fc2fc158a155?from=pc获取令牌失败
https://p6-tt.byteimg.com/large/pgc-image/c5af6dc52c0a4eb08ea64ae65bbe9143?from=pc
到此为止,主要接口及方法已经讲解一遍!举一反三,触类旁通,学会学习 就很快了!下一步,将接口部署到服务器下,进行访问即可!在管理客户端调用接口,根据返回json数据进行解析(了解json前端知识ajax)进行各种操作即可!有机会时间 会继续分享如何前端验证(ajax)解析数据并展现到页面上(H5)实现的功能一览表此登录暂未使用接口,模拟的自动登录设备接线调试方法
你能直接告诉我你从哪搬运来的嘛?我觉得去直接看原版可能好一些 有些截图是边写边截图的,故有些图片是复制过来的。保证每一个字都是自己打出来的!昨晚连续更新4篇文字,2个平台同步更新的,昨晚发布于TT,次发于CSDN。今天更新于次!不喜勿喷,违规删!感谢您的理解。
编辑一篇文字也不容易。一个平台截图一遍不现实呢。部分图片复用的哦。保证是本人的原创!一些非必要的图片均已省略!
考虑到版规,不能发外部链接,所以 部分代码复制时难免会乱!深表歉意,代码部分我重新粘贴整理了。
省略一些文字。
关于服务端部署的看TT里一篇i6860470660372791816可以参考下。谢谢 麻烦老哥给个搬运连接,这排版没法看 田田爱崽崽 发表于 2020-8-14 10:15
你能直接告诉我你从哪搬运来的嘛?我觉得去直接看原版可能好一些
https://blog.csdn.net/uaime/article/details/107991935 老狗丶 发表于 2020-8-14 10:18
https://blog.csdn.net/uaime/article/details/107991935
感谢老哥 这排版无敌了 老狗丶 发表于 2020-8-14 10:16
麻烦老哥给个搬运连接,这排版没法看
我的首发在TT(CSDN同步4篇),连续更新了5篇,部分图片没法重复截取只能复用。代码重新整理了。 bachelor66 发表于 2020-8-14 10:22
这排版无敌了
论坛的浏览器用起来非常不爽,刚才发一篇花费了20多分钟还乱糟糟 准备删除重发,后来没法删除重新编辑了一下,代码重新用本论坛的插入代码方法。
由于图片较多,边写边截图,部分图片 没法重新截取只能复用!如果需要可以私信交流,谢谢