2017年12月30日 22:15
原创作品,转载时请务必以超链接形式标明文章原始出处,否则将追究法律责任。

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


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

image.png

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了。我们看一下效果,在看效果之前,先把特定页设置一下

image.png

再把默认的路由设置一下

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之后就是登录界面。

image.png


这就是用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请求,在登录失败后,弹出错误信息

image.png 

在登录成功后,跳转至主页面。$("#div_login_process")这个div是用来展示登录进度的,效果如上图,就是这么简单。


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

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

image.png

确实是这样,大家注意到这个用户名文本框我设置了一个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, JsonMsgType jsonMsgType = JsonMsgType.FAIL)
    {
        return Json(new { suc = (int)jsonMsgType, msg = MessageResourceBuilder.GetMessageResource(msg) }, JsonRequestBehavior.AllowGet);
    }

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

public enum JsonMsgType
{
    SUCCESS = 1,
    FAIL = 0
}

用于根据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";
    }
    else {
        $("#div_login_process").hide();
        alert(data.msg, "提示信息");
        $scope.getValidateCode();
    }
},

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

image.png

发表评论
匿名  
用户评论
暂无评论