以前发短信使用过短信猫,现在,更多地是使用第三方API。大致过程是:

→ 用户在页面输入手机号码
→ 用户点击"获取验证码"按钮,把手机号码发送给服务端,服务端产生几位数的随机码,并保存在某个地方(Session, Applicaiton, 数据库, 等等),调用第三方的API
→ 第三方发送几位数的随机码至用户手机
→ 用户在页面输入接收到的随机码
→ 把随机码等发送给服务端,与服务端保存的随机码比较,如果一致,就通过,让注册

就按如下界面来说吧:

我们需要考虑的方面包括:

● 手机号码:判断手机号码的合法性,与数据库中已有的手机号码比较,判断是否有重复,等等
● "获取短信验证码"按钮:点击后,禁用它,再来一个比如60秒的倒计时,倒计时结束恢复使用
● "提交"按钮:在提交之前需要判断表单是否验证通过,以及验证码是否通过
● 点击"获取短信验证码"按钮的次数:比如,需要限制来自同一个IP,每天只能点击这个按钮3次

选择验证码提供方、前期准备

本人选择了"云之讯":http://www.ucpaas.com/

注册成为"云之讯"的用户。

进入"云之讯"管理后台,首页就可看到开发者信息,包括Account Sid, AuthToken, Rest URL,这些将来都会用到。

依次点击"应用管理","应用列表",右侧页面的"创建应用",创建成功后将会分配到一个应用ID,这个应用ID也会被用到。另外,创建的应用需要"云之讯"审核通过后,才可以在本地调试。

依次点击"应用管理","短信管理",右侧页面的"添加模版", 在"添加模版"页,"内容"这项应该类似这样填写:您注册{1}网站的验证码为{2},请于{3}分钟内正确输入验证码,添加短信模版成功后会分配到一个模版ID,这个模版ID也会被用到,创建的短信模版也需要"云之讯"审核通过后,才可以在本地调试。

好了,再来总结一下,我们在开发的时候需要哪些信息。包括:

● Rest URL
● Account Sid
● AuthToken
● 应用ID
● 短信模版ID
● 短息模版的内容,比如"您注册{1}网站的验证码为{2},请于{3}分钟内正确输入验证码",{1},{2},{3}是占位符,在应用程序中我们只需要拼接出一个以英文逗号隔开的字符串就可以,比如"我的网站,我的验证码,1"

先下载一个C#的Demo,在这里下载:http://www.ucpaas.com/product_service/download,   在"REST Server Demo"中可以找到。

另外,"云之讯"C#版Demo是使用HttpWebRequest,向API发出请求的,在ASP.NET MVC中,我们也可以使用HttpClient向API发出请求,请求格式参考如下:

http://docs.ucpaas.com/doku.php?id=rest_api%E4%BB%8B%E7%BB%8D
http://docs.ucpaas.com/doku.php?id=%E7%9F%AD%E4%BF%A1%E9%AA%8C%E8%AF%81%E7%A0%81_%E6%A8%A1%E6%9D%BF%E7%9F%AD%E4%BF%A1

在开发的时候,有时需要查看返回的状态码,查看这里:http://docs.ucpaas.com/doku.php?id=rest_error

在ASP.NET MVC下开发

对于用户注册相关的视图模型,给出如下一个类。

    public class UserInputVm
    {
        [Required(ErrorMessage = "必填")]
        [StringLength(16, ErrorMessage = "长度1-16位")]
        [Display(Name = "请输入手机号")]
        [RegularExpression(@"^1[3458][0-9]{9}$", ErrorMessage = "手机号格式不正确")]
        public string LoginName { get; set; }
    }

以上,只考虑了手机输入的合法性,现实中,还需要判断用户输入的手机号是否和数据库中已有的重复,用到一个远程验证特性,参考这里:http://www.cnblogs.com/darrenji/p/3578133.html

在项目根目录下创建一个"Extension"文件夹,并创建一个UCSRestRequest类,把"云之讯"C#版Demo中的UCSRestRequest类下的内容以及EBodyType枚举一同拷贝下来。

在HomeController下,Index方法送出一个视图模型实例。

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(new UserInputVm());
        }

        ......
    }

在Home/Index.cshtml视图中,点击"发送验证码"按钮,让该按钮倒计时,并把手机号发送给服务端;点击"提交"按钮,判断验证表单通过之后,再向服务端发送用户填写的验证码,验证码通过之后才提交表单信息。

@model MvcApplication2.Models.UserInputVm

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Index</h2>

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "addForm" }))
{
    @Html.LabelFor(m => m.LoginName)
    @Html.TextBoxFor(m => m.LoginName)
    @Html.ValidationMessageFor(m => m.LoginName)
    <span id="success"></span>

    <br />
    <br />
    <span>请输入验证码</span>
    <input type="text" id="myCode" />
    <span id="codehint"></span>
    <input type="button" id="getCode" value="点击获取验证码" />

    <br />
    <br />
    <input type="button" id="up" value="提交" />
}

@section scripts
{
    <script type="text/javascript">

        $(function () {
            //点击发送验证码
            $('#getCode').on("click", function () {

                checkGetCodeBtn();

                $.post('@Url.Action("GetCheckNum", "Home")', { 'phoneNum': $('#LoginName').val() }, function (data) {
                    if (data.msg) {
                        $('#success').text("已发送验证码");
                        alert(data.content);
                    } else {
                        var $getCodeBtn = $('#getCode');
                        clearInterval(t);
                        $getCodeBtn.prop('disabled', false);
                        $getCodeBtn.val("点击获取验证码");
                        count = 60;
                        $('#success').text("");
                    }
                });
            });

            //提交
            $('#up').on("click", function () {

                //表单验证通过才验证验证码的正确性
                if ($('#addForm').valid()) {

                    //clearInterval(t);

                    $.post('@Url.Action("CheckCode", "Home")', { 'checkNum': $('#myCode').val() }, function (data) {
                        if (data.msg) {//验证码匹配

                            alert(data.content);

                            $.ajax({
                                cache: false,
                                url: '@Url.Action("Index", "Home")',
                                type: 'POST',
                                dataType: 'json',
                                data: $('#addForm').serialize(),
                                success: function (result) {
                                    if (result.msg) {
                                        alert(result.content);
                                    }
                                },
                                error: function (xhr, status) {
                                    alert("提交失败,状态码:" + status);
                                }
                            });
                        } else {
                            $('#codehint').text(data.content);
                        }
                    });
                }

            });
        });

        var count = 60; //计时开始
        var t; //时间间隔种子
        var isPass = false;//验证码是否输入正确
        function checkGetCodeBtn() {
            //关于按钮
            var $getCodeBtn = $('#getCode');

            t = setInterval(function () {
                $getCodeBtn.val(count + "秒之后重新获取");
                $getCodeBtn.prop('disabled', true);
                count--;

                if (count == 0) {
                    clearInterval(t);
                    $getCodeBtn.prop('disabled', false);
                    $getCodeBtn.val("点击获取验证码");
                    count = 60;
                    $('#success').text("");
                }

            }, 1000);

        }

    </script>
}


再回到HomeController,有一个方法用来接收用户的手机号,产生并保存随机码,调用API;有一个方法用来接收用户的短信验证码,判断是否匹配;当然还有一个接收表单数据的方法。

    public class HomeController : Controller
    {

        ......

        [HttpPost]
        public ActionResult Index(UserInputVm userInputVm)
        {
            if (ModelState.IsValid)
            {
                //实际上这里要做数据库保存工作
                //return PartialView("DisplayUser", userInputVm);
                return Json(new {msg=true,content=userInputVm.LoginName});
            }
            else
            {
                return View(userInputVm);
            }

        }


        //让api把验证码发给用户手机
        [HttpPost]
        //[EnableThrottling(PerSecond = 1, PerMinute = 1, PerHour = 3, PerDay = 3)]
        public ActionResult GetCheckNum(string phoneNum)
        {
            //TODO: 在这里再次判断用户手机号是否与数据库中已有的重复

            //产生随机验证码
            Random r = new Random();
            string temMsg = string.Empty;
            for (int i = 0; i < 4; i++)
            {
                temMsg += r.Next(0, 9);
            }

            //保存随机码
            //Session["num"] = temMsg;
            //ControllerContext.HttpContext.Application["num"] = temMsg;
            //TODO: 这里建议随机码保存到数据库,因为调用API发送短信,Session,Application状态会丢失

            //拼接短信内容
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("{0},{1},{2}", "就依你", temMsg, "1"); //您注册{1}网站的验证码为{2},请于{3}分钟内正确输入验证码

            #region Demo

            string serverIp = "api.ucpaas.com";
            string serverPort = "443";
            string account = "这里填写开发者的Account Sid";     //用户sid
            string token = "这里填写开发者的AuthToken";         //用户sid对应的token
            string appId = "这里填写审核通过上线应用的Id";      //对应的应用id,非测试应用需上线使用
            string templatedId = "这里填写短信模版ID";          //短信模板id,需通过审核
            //string clientNum = "60000000000001";
            //string clientpwd = "";
            //string friendName = "";
            //string clientType = "0";
            //string charge = "0";
            //string phone = "";
            //string date = "day";
            //uint start = 0;
            //uint limit = 100;
            //string toPhone = "";                                    //发送短信手机号码,群发逗号区分
            //string param = "";                                     //短信参数 a,b,c
            //string verifyCode = "1234";
            //string fromSerNum = "4000000000";
            //string toSerNum = "4000000000";
            //string maxallowtime = "60";

            UCSRestRequest api = new UCSRestRequest();

            api.init(serverIp, serverPort);
            api.setAccount(account, token);
            api.enabeLog(true);
            api.setAppId(appId);
            api.enabeLog(true);
            string feedback = api.SendSMS(phoneNum, templatedId, sb.ToString());
            #endregion

            return Json(new { msg = true, content = feedback });
        }

        //检测验证码
        [HttpPost]
        public ActionResult CheckCode(string checkNum)
        {
            try
            {
                //实际需要从数据库中获取保存的随机短信验证码
                //实际,这里的Application["num"]或Session["num"]数据状态已经丢失了
                if (ControllerContext.HttpContext.Application["num"] != null)
                {

                    string temp = ControllerContext.HttpContext.Application["num"].ToString();
                    if (checkNum == temp)
                    {
                        return Json(new { msg = true, content = temp });
                    }
                    else
                    {
                        return Json(new { msg = false, content = "验证码不匹配请重新输入" });
                    }
                }
                else
                {
                    return Json(new { msg = false, content = "验证码失效请重新获取" });
                }

            }
            catch (Exception ex)
            {

                throw;
            }
        }
    }


以上,

● [HttpPost]下的Index方法,用来接收保存表单数据。

● GetCheckNum方法接收用户手机号,需要提醒的是:

1、EnableThrottling特性是用来限制在单位时间间隔之内,来自同一个IP请求的次数,使用方法参考这里:http://www.cnblogs.com/darrenji/p/4446767.html

2、不要把随机短信验证码保存到Session或Application中,因为Demo中实际使用HttpWebRequest向API发出请求,另开了一个线程,Session或Application状态会丢失,建议把随机短信验证码保存到数据库中。

3、虽然前端页面中可以判断用户输入手机号是否与数据库中已有的重复,但用户还是有可能把重复的手机号发送到服务端来,所以,在服务端的本方法内,还是有必要再次判断用户的手机号是否重复。

4、也可以不使用Demo中提供的方法向API发出请求,使用HttpClient同样可以,不过需要字节拼接和设置,需要对HttpClient的使用有一定的了解。

● CheckCode方法用来接收用户的短信验证码,建议把接收的验证码和数据库中该手机号的验证码对比。

最后,♥谢谢UIT工作室推荐了"云之讯"并提供了HttpClient调用的代码参考,♥谢谢"云之讯"团队成员的配合,他/她们是:Cathy@云之讯,Ada@云之讯, Auspicious@云之讯,等等♥。

在ASP.NET MVC下通过短信验证码注册的更多相关文章

  1. ASP.NET MVC+Bootstrap 实现短信验证

    短信验证大家都已经非常熟悉了,基本上每天都在接触手机短信的验证码,比方某宝,某东购物.站点注冊,网上银行等等,都要验证我们的手机号码真实性.这样做有什么优点呢. 曾经咱们在做站点的时候.为了提高用户注 ...

  2. 基于tp3.2的腾讯云短信验证码的实现

    新手小白在公司要完成短信验证码注册功能,最初由于没有经验,网上的教程又不是很全,便参考着官方API文档,进行开发 直接进入正题:使用composer下载腾讯云短信接口(记得添加依赖).在项目目录下新建 ...

  3. asp.net mvc 短信验证码

    把发短信功能写成一个类包,需要引用: SmsUtillity.cs: using System; using System.Collections.Generic; using System.Linq ...

  4. SNF快速开发平台MVC-EasyUI3.9之-ueditor富文本编辑在 asp.net MVC下使用步骤

    mvc项目中用到了这个富文本编辑就试着把遇到的问题个使用步骤在这里记录一下,希望大家少走弯路. 1.首先我们先下载net版本的uediot 2.然后把整个文档拷贝到我们的项目中,记得是整个 把下载的文 ...

  5. ASP.NET MVC下的四种验证编程方式[续篇]

    在<ASP.NET MVC下的四种验证编程方式>一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式("手工验证"."标注Validation ...

  6. ASP.NET MVC下的四种验证编程方式

    ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效性,我们将针对参数的验证成为Model绑定 ...

  7. Response.End()在Webform和ASP.NET MVC下的表现差异

    前几天在博问中看到一个问题--Response.End()后,是否停止执行?MVC与WebForm不一致.看到LZ的描述后,虽然奇怪于为何用Response.End()而不用return方式去控制流程 ...

  8. ASP.NET MVC下的四种验证编程方式[续篇]【转】

    在<ASP.NET MVC下的四种验证编程方式> 一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式(“手工验证”.“标注ValidationAttribute特性”.“ ...

  9. ASP.NET MVC下的四种验证编程方式【转】

    ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效 性,我们将针对参数的验证成为Model绑 ...

随机推荐

  1. 奈奎斯特定理 and 香农定理

    -----------------------整理自<21ic电子网> 奈奎斯特定理(Nyquist's Theorem)和香农定理(Shannon's Theorem)是网络传输中的两个 ...

  2. 特性(C# 和 Visual Basic)

    特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集.类型.方法.属性等)相关联. 特性与程序实体关联后,即可在运行时使用名为“反射”的技术查询特性. 有关更多信息,请参见 反射(C# 和 V ...

  3. ASP .Net Core系统部署到SUSE Linux Enterprise Server 12 SP3 64 具体方案

    .Net Core 部署到 SUSE Linux Enterprise Server 12 SP3 64 位中的步骤 1.安装工具 1.apache 2..Net Core(dotnet-sdk-2. ...

  4. ssh隐藏的sftp功能的使用

    sftp是Secure File Transfer Protocol的缩写,安全文件传送协议.可以为传输文件提供一种安全的加密方法.sftp 与 ftp 有着几乎一样的语法和功能.SFTP 为 SSH ...

  5. Android仿苹果版QQ下拉刷新实现(二) ——贝塞尔曲线开发"鼻涕"下拉粘连效果

    前言 接着上一期Android仿苹果版QQ下拉刷新实现(一) ——打造简单平滑的通用下拉刷新控件 的博客开始,同样,在开始前我们先来看一下目标效果: 下面上一下本章需要实现的效果图: 大家看到这个效果 ...

  6. VSCode配置简单的vue项目

    VSCode配置简单的vue项目 https://www.cnblogs.com/wnxyz8023/p/9989447.html 由于最近要使用的项目框架为前后端分离的,采用的是vue.js+web ...

  7. VS C++ 并发编程

    1.VS2012及以上版本,支持C++11 thread类的并发编程. 相关材料可以参考博客:http://www.cnblogs.com/rangozhang/p/4468754.html 2.但对 ...

  8. Storm1.0.3集群部署

    Storm集群部署 所有集群部署的基本流程都差不多:下载安装包并上传.解压安装包并配置环境变量.修改配置文件.分发安装包.启动集群.查看集群是否部署成功. 1.所有的集群上都要配置hosts vi   ...

  9. 【python学习-1】python环境设置与开发

    开始学习python,打算把学习过程都记下来. 下载python,虽然推荐官网,但是感觉官网上面下载python太慢,所以我最后是在csdn上面下载的python版本(3.2.4 windows 64 ...

  10. 当Java遇到XML 的邂逅+dom4j

    XML简介: XML:可扩展标记语言! 01.很象html 02.着重点是数据的保存 03.无需预编译 04.符合W3C标准 可扩展:我们可以自定义,完全按照自己的规则来! 标记: 计算机所能认识的信 ...