第一章【API接口设计与本地调试】

本章为【WebApp全栈项目开发】实战的第一章,主要描述一个WebAPI接口是如何构建的。

在构建WebAPI接口前,我们首先要创建一个数据库,起名为APP_DBA,然后创建相应的表,分别为APP、APP_Class、APP_ClassOne、APP_ClassTwo、APP_User。

分类表结构.png
应用表.png

数据库建好之后,我们先手动添加一些测试数据,之后我们会编写一个WinForm客户端来上传真实的数据。

接着我们在VS中新建一个项目ASP.NET Web应用程序

1.png

然后选择空模板并勾选Web API

2.png

在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算法知识请看下面这篇文章

http://www.cnblogs.com/wangxin201492/p/5030943.html

2、由于TOTP算法的源码多数为Javascript和Python版本,没有找到C#版的,所以就添加微软的Microsoft Script Control 1.0这个引用,用C#去调用Javascript脚本

专门为伸手党提供totp.js源码 https://pan.baidu.com/s/1kUZViab

js.png

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;
        }

    }
}

Controller.png

最后修改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();
json.png

这样一个WebAPI接口就编写完成了,接下来就是发布。

在你的项目上右键选择发布,目标选择IIS、FTP等

iis.png

发布方法选择【文件系统】然后指定输出的路径

is.png

将输出的文件上传到服务器,并部署到IIS上就可以通过外网来访问了。具体操作放到下一章《服务器部署》中讲述。

我们先在本地测试一下这个接口,假定某一个用户的Token我们已知为LFLFMU2SGVCUIUCZKBMEKRKLIQ,然后我们可以通过nodejs或者构建一个静态页面来调用totp.js从而计算出我们的密钥Key

我这里计算出来的结果是006156,带入到接口请求参数中得到
http://localhost:51230/ajax/app/GetAjax?key=006156&uid=1

json.png

接口项目源码 https://pan.baidu.com/s/1qYtxNhM

本章介绍完毕请看下一章《服务器部署》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,642评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,781评论 6 342
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,887评论 25 707
  • 总有一天 我的床头有你随意翻看的书 卫生间的剃须刀旁是你的粉底液 更衣室的白衬衫里夹杂着你的白裙 车副坐成了你的专...
    就爱嘚吧嘚阅读 122评论 0 0
  • 忘了在哪里看到的图片,很是喜欢。 图片美得像童话一般,怎么能把自己的生活活得像童话一样呢? 前天和一个55岁的老男...
    孔雀东南飞飞阅读 702评论 2 17