本章为【WebApp全栈项目开发】实战的第一章,主要描述一个WebAPI接口是如何构建的。
在构建WebAPI接口前,我们首先要创建一个数据库,起名为APP_DBA,然后创建相应的表,分别为APP、APP_Class、APP_ClassOne、APP_ClassTwo、APP_User。
数据库建好之后,我们先手动添加一些测试数据,之后我们会编写一个WinForm客户端来上传真实的数据。
接着我们在VS中新建一个项目ASP.NET Web应用程序
然后选择空模板并勾选Web API
在Web.config中添加数据库连接
<connectionStrings>
<add name="AppConnection" connectionString="Data Source=120.25.77.100;Initial Catalog=APP_DBA;User ID=sa;Password=********" providerName="System.Data.SqlClient"/>
</connectionStrings>
注意一定要用SQL Server身份来连接数据库,否则部署到IIS上,IIS是识别不了的。
接着我们需要构建一个SQL类用来查询数据库里的内容,在Models文件夹右键添加类,类文件源码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.IO;
using MSScriptControl;
using System.Data.SqlClient;
using System.Configuration;
namespace AppAjaxApi.Models
{
public class SQL
{
//获取连接字符串
static string str = ConfigurationManager.ConnectionStrings["AppConnection"].ConnectionString;
//根据ID查询用户的Token
public DataSet GetToken(int uid)
{
string cmdText = "select * from APP_User where ID=@ID";
SqlConnection con = new SqlConnection(str);
SqlCommand com = new SqlCommand(cmdText, con);
SqlParameter[] ps = { new SqlParameter("ID", uid) };
com.Parameters.AddRange(ps);
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter(com);
da.Fill(ds);
con.Close();
return ds;
}
//根据Token计算密钥Key
public int GetKey(string Token)
{
string js = AppDomain.CurrentDomain.BaseDirectory + "App_Data\\totp.js";
string cjs = File.ReadAllText(js);
string fun = string.Format(@"getCode('{0}')", Token);
string result = ExecuteScript(fun, cjs);
return Convert.ToInt32(result);
}
//根据APP名称查询APP数据
public DataSet GetApp(string name)
{
string cmdText = "select Icon,Name,ClassTwoName,Score,Count,Subtitle,Describe,Url from APP,APP_ClassTwo where ClassTwoID=ClassTwo and Name=@Name";
SqlConnection con = new SqlConnection(str);
SqlCommand com = new SqlCommand(cmdText, con);
SqlParameter[] ps = { new SqlParameter("Name", name) };
com.Parameters.AddRange(ps);
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter(com);
da.Fill(ds);
con.Close();
return ds;
}
//根据APP父类查询APP数据
public DataSet GetClassOne(int classone)
{
string cmdText = "select Icon,Name,Score,Count,Subtitle,Url from APP where ClassOne=@ClassOne";
SqlConnection con = new SqlConnection(str);
SqlCommand com = new SqlCommand(cmdText, con);
SqlParameter[] ps = { new SqlParameter("ClassOne", classone) };
com.Parameters.AddRange(ps);
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter(com);
da.Fill(ds);
con.Close();
return ds;
}
//获取所有APP数据
public DataSet Get()
{
string cmdText = "select Icon,Name,Score,Count,Subtitle,Url from APP";
SqlConnection con = new SqlConnection(str);
SqlCommand com = new SqlCommand(cmdText, con);
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter(com);
da.Fill(ds);
con.Close();
return ds;
}
//C#调用javascript方法
public string ExecuteScript(string Expression,string Code)
{
ScriptControl scriptControl = new ScriptControl();
scriptControl.UseSafeSubset = true;
scriptControl.Language = "JScript";
scriptControl.AddCode(Code);
try
{
string str = scriptControl.Eval(Expression).ToString();
return str;
}
catch(Exception ex)
{
string str = ex.Message;
}
return null;
}
}
}
这里需要注意两个事情
1、为了防止接口被恶意盗用,本接口采用TOTP算法实现了客户端和服务端的双向认证,即客户端登录后获取用户的Token,并根据这个Token在请求接口的时候采用TOTP算法计算出一个密钥Key,包含到请求中一并发送给服务端。服务端接收请求参数的同时也会去计算密钥Key,然后和客户端传过来的Key进行比较,如果一致说明是合法请求,不一致不会返回数据。
了解更多TOTP算法知识请看下面这篇文章
2、由于TOTP算法的源码多数为Javascript和Python版本,没有找到C#版的,所以就添加微软的Microsoft Script Control 1.0
这个引用,用C#去调用Javascript脚本
专门为伸手党提供totp.js源码 https://pan.baidu.com/s/1kUZViab
SQL类创建完成后我们还需要创建一个控制器。在右侧的Controllers文件夹上右键添加控制器,这里取名为APPControllers。代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Data;
using System.Web.Http;
using AppAjaxApi.Models;
namespace AppAjaxApi.Controllers
{
public class APPController : ApiController
{
public SQL sql = new SQL();
//根据APP名称返回APP数据
public DataTable GetApp(string name, int key, int uid)
{
string Token = sql.GetToken(uid).Tables[0].Rows[0]["Token"].ToString();
int Key = sql.GetKey(Token);
if (Key == key)
{
return sql.GetApp(name).Tables[0];
}
return GetMsg();
}
//根据父类返回APP数据
public DataTable GetAjaxByClassOne(int classone, int key, int uid)
{
string Token = sql.GetToken(uid).Tables[0].Rows[0]["Token"].ToString();
int Key = sql.GetKey(Token);
if (Key == key)
{
return sql.GetClassOne(classone).Tables[0];
}
return GetMsg();
}
//返回所有APP数据
public DataTable GetAjax(int key,int uid)
{
string Token = sql.GetToken(uid).Tables[0].Rows[0]["Token"].ToString();
int Key = sql.GetKey(Token);
if (Key == key)
{
return sql.Get().Tables[0];
}
return GetMsg();
}
//非法调用接口返回数据
public DataTable GetMsg()
{
DataTable dt = new DataTable();
DataColumn dc = new DataColumn("msg", Type.GetType("System.String"));
dt.Columns.Add(dc);
DataRow dr = dt.NewRow();
dr["msg"] = "非法调用接口";
dt.Rows.Add(dr);
return dt;
}
}
}
最后修改App_Start文件夹下的接口配置文件,使返回的数据为JSON格式
在Register方法内添加如下代码
// Web API 配置和服务
config.Formatters.Remove(config.Formatters.XmlFormatter);
var jsonFormatter = config.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
这样一个WebAPI接口就编写完成了,接下来就是发布。
在你的项目上右键选择发布,目标选择IIS、FTP等
发布方法选择【文件系统】然后指定输出的路径
将输出的文件上传到服务器,并部署到IIS上就可以通过外网来访问了。具体操作放到下一章《服务器部署》中讲述。
我们先在本地测试一下这个接口,假定某一个用户的Token我们已知为LFLFMU2SGVCUIUCZKBMEKRKLIQ
,然后我们可以通过nodejs或者构建一个静态页面来调用totp.js从而计算出我们的密钥Key
我这里计算出来的结果是006156
,带入到接口请求参数中得到
http://localhost:51230/ajax/app/GetAjax?key=006156&uid=1