ASP.NET MVC4+BootStrap 实战(五)

从本篇文章开始,我们就开始实战头条网后台管理。记得我写过webApp实战一,里面提到了头条网,我打算围绕这个头条网做一个后台管理系统,然后再实现头条网移动站点。因为现在公司也迟迟不能进行web项目的开发,一直都是银光,这样下去,恐怕我把web开发都忘光了。所以不管是J2EE还是ASP.NET总要持续学习,否则你就跟不上时代了。


废话不多说了,我们先看一下头条网后台管理Solution。

wKiom1SmRsKSNxXuAADdz4QlXgo944.jpg

OK,三层架构,最简单的架构。我们现在看一下第一个页面

@{
    Layout = null;
}
<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>登录</title>
    <link rel="stylesheet" type="text/css" href="~/BootStrap/css/bootstrap.min.css" />
    <link rel="stylesheet" type="text/css" href="~/BootStrap/css/bootstrap-theme.css" />
    <link rel="stylesheet" type="text/css" href="~/Content/Login.css" />
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootStrap")
    <script type="text/javascript" src="~/Scripts/angular.js"></script>
</head>
<body>
    <div id="main_layout" ng-app="loginModule" ng-controller="loginController">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">
                    <img src="~/Images/Base/userlogin.png" style="width:25px;height:25px" />
                    <b>用户登录</b>
                </h3>
            </div>
            <form ng-submit="loginIn()" role="form">
                <div class="div-panel-content">
                    <div class="row">
                        <div class="col-md-12 col-md-margin">
                            <div class="input-group">
                                <span class="input-group-addon bg_user">
                                    <img src="~/Images/Base/usermng.jpg" />
                                </span>
                                <input ng-model="userNo" type="text" class="form-control" autofocus="autofocus" placeholder="请输入用户名" maxlength="15" required>
                            </div>
                        </div>
                        <div class="col-md-12 col-md-margin">
                            <div class="input-group">
                                <span class="input-group-addon bg_pwd">
                                    <img src="~/Images/Base/pwd.jpg" />
                                </span>
                                <input ng-model="pwd" type="password" class="form-control" placeholder="请输入密码" required maxlength="15">
                            </div>
                        </div>
                        <div class="col-md-12 col-md-margin form-inline">
                            <div class="input-group col-md-8">
                                <span class="input-group-addon bg_pwd">
                                    <img src="~/Images/Base/validateCode.png" />
                                </span>
                                <input ng-model="validateCode" type="text" class="form-control" placeholder="请输入验证码" required maxlength="5">
                            </div>
                            <div class="col-md-4" style="float:right">
                                <img id="img_ValidateCode" src="../../Handler/ValidateCodeCreate.ashx" ng-click="getValidateCode()">
                            </div>
                        </div>
                        <div class="col-md-12 col-md-margin" style="text-align:center;margin-bottom:10px">
                            <button type="submit" class="btn btn-primary">登录</button>
                            <button type="reset" class="btn btn-primary">取消</button>
                        </div>
                        <div id="div_login_process" class="col-md-12 col-md-margin" style="text-align:center;display:none">
                            <img src="~/Images/Base/loading.gif" />
                            <label style="color: #003399">正在登录,请稍候......</label>
                        </div>
                    </div>
                </div>
            </form>
        </div>
    </div>
    <script>
        var appModule = angular.module('loginModule', []);
        appModule.controller("loginController", function ($scope) {
            $scope.loginIn = function () {
                $.ajax({
                    url: "/Login/LoginIn",
                    type: "POST",
                    dataType: "json",
                    data: {
                        requestJson: JSON.stringify({
                            userNo: $.trim($scope.userNo),
                            pwd: $.trim($scope.pwd),
                            validateCode:$.trim($scope.validateCode)
                        })
                    },
                    beforeSend: function () {
                        $("#div_login_process").show();
                    },
                    complete: function () {
                        $("#div_login_process").hide();
                    },
                    success: function (data) {
                        if (data.suc == 1) {
                            window.location.href = "/Home/Index";
                        }
                        else {
                            $("#div_login_process").hide();
                            alert(data.msg, "提示信息");
                        }
                    },
                    error: function () {
                        $("#div_login_process").hide();
                    }
                });
            }

            $scope.getValidateCode = function () {
                $("#img_ValidateCode").attr("src", "../../Handler/ValidateCodeCreate.ashx?param=" + new Date().toTimeString());
            }
        });
    </script>
</body>
</html>

这个是登录界面,我们在这里引用了bootStrap,安哥拉杰斯(angularjs)。这个页面我们将一个div放在浏览器的正中间,然后在这个div中使用BootStrap布局。

<div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">
                    <img src="~/Images/Base/userlogin.png" style="width:25px;height:25px" />
                    <b>用户登录</b>
                </h3>
            </div>
            <form ng-submit="loginIn()" role="form">
                <div class="div-panel-content">

先看这个class panel这个其实是BootStrap提供的样式,在这个Panel中我们先设置panel-title,再设置panel-content。在Panel Content中我们布局我们的登录表单。关于UI布局我在这里就不多说了,给大家介绍两个网站,大家自己去看

BootStrap:

http://www.w3cschool.cc/bootstrap/bootstrap-tutorial.html

http://v3.bootcss.com/components/?#media

这两个网站就够你学BootStrap了,以后关于UI布局我不会再多说了,反正用到了就去这两个网站看就行了。不过在这里我只说一下这个div居中浏览器的css

#main_layout {
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -130px 0 0 -165px;
    width: 330px;
    height: 260px;
}

距离上左各50%,然后让左边距和上边距分别为宽度和高度的负一半就ok了。我们看一下效果,在看效果之前,先把特定页设置一下

wKiom1SmS4izeXMPAADJrClmIQY103.jpg

再把默认的路由设置一下

public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Login", action = "Login", id = UrlParameter.Optional }
            );
        }
    }

这样当部署以后,直接IP:port之后就是登录界面。

wKioL1SmTL7AqMjSAAaCS6rwtWM488.jpg

wKiom1SmTC2QIADfAAFmov1b5f4794.jpg

这就是用BootStrap布局出来的页面,OK,我们看一下是如何实现登录的。

首先我们在点击登录按钮的时候,会提交form到Login/LoginIn这个action。那么我们的提交是如何验证的,我们知道,如果单纯的使用ajax提交,意味着html5自带的验证就不会起作用,所以我们使用angularjs。在页面代码的div中,我们有如下设置

 <div id="main_layout" ng-app="loginModule" ng-controller="loginController">

我们设置模块为loginModule,这个module其实是标示一个范围用的,假如你在一个页面有两部分想使用不同的$scope,那么你就可以设两个ng-app,而他们中的控制器和变量是互不影响的。在form标签中,我们看到有一个ng-submit

 <form ng-submit="loginIn()" role="form">

这个意思是当提交的时候,去调用$scope中的loginIn方法。所以在页面底部的js中,在controller中有个方法叫loginIn。

 $scope.loginIn = function () {},在这个方法中ajax请求发送了requestJson的请求数据。

requestJson: JSON.stringify({
                            userNo: $.trim($scope.userNo),
                            pwd: $.trim($scope.pwd),
                            validateCode:$.trim($scope.validateCode)
                        })

在这里其实是取得$scope中的三个表单绑定的值,怎么绑定的呢,注意这三个input表单,都有一个ng-model的扩展标签,分别和$scope进行关联绑定。如果你引用了安哥拉杰斯,并且在html标签上标记了ng-model标签,那么在VS中当你$scope点的时候,是会有智能提示的。


正是这个方法去发出ajax请求,在登录失败后,弹出错误信息

wKioL1SmTyChqxQbAAEXkBGj3Bo187.jpg wKiom1SmTy-x1oZdAAEqNtFj2HU530.jpg

在登录成功后,跳转至主页面。

$("#div_login_process")这个div是用来展示登录进度的,效果如下,就是这么简单。

wKioL1SmUDui0PcEAADQBbskUG0020.jpg

那么我们在登录的时候,如果用户没有输入用户名或者密码怎么验证的呢,这个不需要你操心,这个是html5自带的,看一下效果,他会提示你请填写此字段,只要你设置了required。如果你给input type="text"设置了title属性,那么它的验证信息就会加上title的内容

<input ng-model="userNo" type="text" class="form-control" title="用户名不能为空" autofocus="autofocus" placeholder="请输入用户名" maxlength="15" required>

wKiom1SmT_aiHKBRAADdNl9lOc0445.jpg wKioL1SmUXHyUPVLAADv_uYW3hs960.jpg

确实是这样,大家注意到这个用户名文本框我设置了一个autofocus,这个标签也是html5的新标签用来设置页面呈现以后,获得焦点的元素。

OK,上面的部分讲完了,到下面的部分,验证码。这个验证码需要在服务端生成,我们看前端代码是调用了一个httpHandler处理文件。

<img id="img_ValidateCode" src="../../Handler/ValidateCodeCreate.ashx" ng-click="getValidateCode()">

在这个img被click的时候调用了$scope中的getValidateCode方法。getValidateCode方法很简单,只不过是重新设置img的src属性。我们主要看这个httpHandler是如何向客户端输出img的。

public class ValidateCodeCreate : IHttpHandler, IRequiresSessionState
    {

        public void ProcessRequest(HttpContext context)
        {
            ValidateCode vCode = new ValidateCode();
            string code = vCode.CreateValidateCode(5);
            context.Session["ValidateCode"] = code;
            byte[] bytes = vCode.CreateValidateGraphic(code);
            MemoryStream ms = new MemoryStream(bytes);
            Bitmap bitmap = new Bitmap(ms);
            bitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
            context.Response.ContentType = "image/jpg";
            context.Response.End();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }

在这里我们通过调用一个ValidateCode类,去获取一个5位的验证码图片。并且把生成的验证码存放在session中,用户验证客户端提交的验证码。关于这个C#生成validateCode的方法一大堆,我在这里就不贴出来代码了。好的,到此界面上的所有逻辑就讲完了,接下来我们看一下控制器。

public class LoginController : BaseController
    {
        public ViewResult Login()
        {
            return View("~/Views/Home/Login.cshtml");
        }

        [HttpGet]
        public RedirectToRouteResult LogOut()
        {
            Session.Clear();
            return RedirectToAction("Login");
        }

        [HttpPost]
        public ActionResult LoginIn(FormCollection fc)
        {
            string requestJson = fc["requestJson"];
            JObject jObj = (JObject)JsonConvert.DeserializeObject(requestJson);
            string userNo = jObj.Value<string>("userNo");
            string pwd = jObj.Value<string>("pwd");
            string validateCode = jObj.Value<string>("validateCode");

            if (string.IsNullOrWhiteSpace(userNo))
            {
                return GetJsonMessage("LG_001");
            }

            if (string.IsNullOrWhiteSpace(pwd))
            {
                return GetJsonMessage("LG_002");
            }

            if (string.IsNullOrWhiteSpace(pwd))
            {
                return GetJsonMessage("LG_004");
            }

            if (!validateCode.Trim().Equals(Session["ValidateCode"]))
            {
                return GetJsonMessage("LG_005");
            }

            bool isLoginSuc = false;
            UserInfoEntity userInfoEntity = UserInfoBiz.GetInstance().GetLoginUser(userNo.Trim(), pwd.Trim(), out isLoginSuc);
            if (isLoginSuc)
            {
                Session["User"] = userInfoEntity;
                return Json(new { suc = 1 });
            }

            return GetJsonMessage("LG_003");
        }
    }

主要有三个action,一个是导向页面的Login,一个是处理登录的LoginIn,一个是处理注销的LoginOut。这里主要是LogIn。我们首先拿到客户端提交的requestJson,这里你既可以使用FormCollection来接收requestJson,也可以直接使用Request["requestJson"]。拿到客户端的请求数据以后,我们使用Newtonsoft.json将其反序列化然后得到每个表单值。验证后,调用Biz层方法去获取用户信息。这里的GetJsonMessage其实是BaseController中的方法

[LoginFilter]
    public class BaseController : Controller
    {
        protected string UserID
        {
            get
            {
                UserInfoEntity userEntity = (Session["User"] as UserInfoEntity);
                return userEntity != null ? userEntity.UserID : string.Empty;
            }
        }

        public JsonResult GetJsonMessage(string msg)
        {
            return Json(new { suc = 0, msg = MessageResourceBuilder.GetMessageResource(msg) }, JsonRequestBehavior.AllowGet);
        }

        public JavaScriptResult GetJSMessage(string msg)
        {
            return JavaScript("alert('" + MessageResourceBuilder.GetMessageResource(msg) + "')");
        }
    }

用于根据MessageID获取Message。我们看一下这个MessageResourceBuilder。

 public class MessageResourceBuilder
    {
        private static Dictionary<string, string> messages;

        public static string GetMessageResource(string resourceID)
        {
            if (messages == null || messages.Count == 0)
            {
                messages = GetAllMessageResource();
            }

            if (!messages.ContainsKey(resourceID))
            {
                throw new Exception(string.Concat("ResourceID:", resourceID, " doesn't exist in message resource file."));
            }

            return messages[resourceID];
        }

        private static Dictionary<string, string> GetAllMessageResource()
        {
            Dictionary<string, string> messageResources = new Dictionary<string, string>();
            string baseFolder = AppDomain.CurrentDomain.BaseDirectory;
            string messageFolder = ConstValues.CONN_MessageResourceFolder;
            string messageResourceFolder = Path.Combine(baseFolder, messageFolder);

            if (!Directory.Exists(messageResourceFolder))
            {
                LogHelper.WriteExceptionLog(string.Concat(MethodBase.GetCurrentMethod().Name, ":", "MessageResource Folder does'nt exist!"));
                return null;
            }

            string[] resourceFiles = Directory.GetFiles(messageResourceFolder, "*.xml", SearchOption.AllDirectories);
            if (resourceFiles.Length == 0)
            {
                LogHelper.WriteExceptionLog(string.Concat(MethodBase.GetCurrentMethod().Name, ":", "MessageResource files don't exist!"));
                return null;
            }

            foreach (var resourceFile in resourceFiles)
            {
                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.Load(resourceFile);

                XmlElement root = xmlDocument.DocumentElement;
                XmlNodeList nodeList = root.SelectNodes("/MessageResources/Message");
                foreach (XmlNode node in nodeList)
                {
                    messageResources.Add(node.Attributes["ResourceID"].Value, node.InnerText);
                }
            }

            return messageResources;
        }
    }

其实是将所有的Message放到Dictionary中,用的时候根据ResourceID取出来。代码很简单,就是获取folder下面的文件,然后循环变化,进行xml解析,加入Dictionary中。我们看一下xml文件的定义,其实很简单。

<?xml version="1.0" encoding="utf-8" ?>
<MessageResources>
  <Message ResourceID="LG_001">
    用户名不能为空!
  </Message>
  <Message ResourceID="LG_002">
    密码不能为空!
  </Message>
  <Message ResourceID="LG_003">
    用户名或者密码不正确!
  </Message>
  <Message ResourceID="LG_004">
    验证码不能为空!
  </Message>
  <Message ResourceID="LG_005">
    验证码不正确!
  </Message>
</MessageResources>

ok,整个登录就说完了,登陆成功后,跳转至Home/Index界面。

success: function (data) {
                        if (data.suc == 1) {
                            window.location.href = "/Home/Index";
                        }

我们看一下登录成功后的效果。

wKiom1SmVc-g-BGOAAMwvuJu3Z8241.jpg就是这么简单,我们下一节讲主界面以及用户注册。

你可能感兴趣的:(AngularJS,bootstrap,asp.net,MVC4,html5验证)