完整JavaWeb项目笔记 第十一部分-登陆页设计

一 需求

  服务端设计已经准备完毕,各类数据访问接口设计都已经实现,前端的页面模板、Js模板也已就绪,剩下最后的Html设计及Js编写工作,这部分没有太多的新鲜技术,而且我个人对前端技术不是很熟练,所以剩下部分我仅针对部分Bootstrap和服务端接口设计进行介绍。

  登录页我们需要提供两个基础功能,登陆和注册,并且通过服务端进行账户验证。这里我偷了个懒,登陆和注册用同一个页面,仅提供两个不同的按钮来做功能上的区分。

  用户注册时可以选填一个昵称,用以登陆后做显示用的用户名,登陆时不需要填写。

  注册时需要账号格式为手机号码,以后有时间我会再提供一个验证码功能,正确填写验证图片中的字符才能获取手机验证码,并且仅手机验证码通过校验才能注册成功,手机验证码通过第三方短信服务实现。这里用到手机号,各位懂得。

  注册时密码格式暂不做太多限制,只要不为空就好,各位可按自己的口味酌情处理。

  登陆时要求账户(手机号码)及密码正确,登陆成功后服务端对用户会话状态进行保存,如果是移动端APP登陆的话有效会话时间为30天,主要不主动退出则会话状态在每次登陆应用时更新,这些都是后话了。

二 页头设计

  第一部分做一个通用的页头,这里使用Bootstrap提供的一个布局组件PageHeader来实现,如下:

<!-- 页头 -->
	<div>
		<div class="container">
			<div class="row clearfix">
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
				<div class="col-sm-12 col-md-10 col-lg-8">
					<div class="page-header">
						<h1>
							简字 <small>简单文字,你的心声</small>
						</h1>
					</div>
				</div>
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
			</div>
		</div>
	</div>

  注意上述代码中的container,因为我们的整体布局是通过Bootstrap的栅格系统实现的,只有将内容置入container内,每行内容才能获得适当的缩放。

  另外,clearfix样式是为了消除在小屏幕浏览时网格错乱而设置的,读者可自行调试下不加入clearfix的缩放区别。

  最后每一列的宽占比通过col-*-*来实现不同屏幕尺寸设备的分辨率来实现不同的展示效果,这部分知识如果读者不是很清楚可以自行百度。

三 主内容区设计

  我习惯把网页除开页头页尾侧边栏等部分,剩余的主体显示区域叫做主内容区。

  主内容区是一个简单的表单,三行输入框,两个功能按钮,先上代码:

<!-- 主内容区 -->
	<div>
		<div class="container">
			<!--内容-->
			<div class="row clearfix">
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
				<div class="col-sm-12 col-md-10 col-lg-8">
                    <form class="form-horizontal">
                        <div class="form-group">
                            <input type="tel" class="form-control" id="inputPhone" placeholder="手机号码(必输)">
                        </div>
                        <div class="form-group">
                            <input type="password" class="form-control" id="inputPassword" placeholder="密码(必输)">
                        </div>
                        <div class="form-group">
                            <input type="text" class="form-control" id="inputNickname" placeholder="昵称(选输)">
                        </div>
                        <div class="form-group">
                            <button type="button" class="btn btn-default" onclick="LoginPageMVC.Controller.login()">登录</button>
                            <button type="button" class="btn btn-default" onclick="LoginPageMVC.Controller.register()">注册</button>
                        </div>
                    </form>
				</div>
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
			</div>
		</div>
	</div>

  整个表单我采用Bootstrap的一个css组件form-horizontal,这是一个水平排列的表单,使用很简单,在父元素上加入form-horizontal样式,把表单组件加入一个带有form-group样式的div中即可,所有的表单控件需要指定样式form-control。

  另外有一点小细节,每一个input的有一个属性叫placeholder,它会以灰底颜色显示提示信息,账户密码的必输提示我没有通过Js的表单验证来处理,一个是自己懒,一个是服务端做了处理,虽然消耗了服务端资源,但是对于这样一个Demo项目来说无可厚非,读者如果有兴趣可以自己尝试一下表单验证,Jquery也提供了很多这方面的插件。

  最后注意一下两个功能按钮的onclick事件,它们直接调用Login.js提供的方法,这部分实现后文介绍。

四 页尾设计

  页尾是固定在页面底部的,不会随着屏幕滚动而发生位置的变化,为了实现这样的需求,我使用了<footer class=“navbar-fixed-bottom”>标签,如下:

<!-- 页尾 -->
	<footer class="navbar-fixed-bottom">
		<div class="container">
			<div class="row clearfix">
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
				<div class="col-sm-12 col-md-10 col-lg-8">
					<address contenteditable="true">
						<strong>冒泡工作室, 大福楠</strong><br /> <abbr title="Phone">Email:</abbr>
						983950935@qq.com
					</address>
				</div>
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
			</div>
		</div>
	</footer>

  完整的效果图参考第九部分,完整的html代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>简字-登陆</title>
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="css/your-style.css" rel="stylesheet">
<!-- 以下两个插件用于在IE8以及以下版本浏览器支持HTML5元素和媒体查询,如果不需要用可以移除 -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
	<!-- 页头 -->
	<div>
		<div class="container">
			<div class="row clearfix">
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
				<div class="col-sm-12 col-md-10 col-lg-8">
					<div class="page-header">
						<h1>
							简字 <small>简单文字,你的心声</small>
						</h1>
					</div>
				</div>
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
			</div>
		</div>
	</div>
	<!-- 主内容区 -->
	<div>
		<div class="container">
			<!--内容-->
			<div class="row clearfix">
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
				<div class="col-sm-12 col-md-10 col-lg-8">
                    <form class="form-horizontal">
                        <div class="form-group">
                            <input type="tel" class="form-control" id="inputPhone" placeholder="手机号码(必输)">
                        </div>
                        <div class="form-group">
                            <input type="password" class="form-control" id="inputPassword" placeholder="密码(必输)">
                        </div>
                        <div class="form-group">
                            <input type="text" class="form-control" id="inputNickname" placeholder="昵称(选输)">
                        </div>
                        <div class="form-group">
                            <button type="button" class="btn btn-default" onclick="LoginPageMVC.Controller.login()">登录</button>
                            <button type="button" class="btn btn-default" onclick="LoginPageMVC.Controller.register()">注册</button>
                        </div>
                    </form>
				</div>
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
			</div>
		</div>
	</div>
	<!-- 页尾 -->
	<footer class="navbar-fixed-bottom">
		<div class="container">
			<div class="row clearfix">
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
				<div class="col-sm-12 col-md-10 col-lg-8">
					<address contenteditable="true">
						<strong>冒泡工作室, 大福楠</strong><br /> <abbr title="Phone">Email:</abbr>
						983950935@qq.com
					</address>
				</div>
				<div class="col-sm-0 col-md-1 col-lg-2"></div>
			</div>
		</div>
	</footer>
	<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
	<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
	<script src="js/login.js"></script>
</body>
</html>

五 Js文件引用

  我在整个html页得最后引入需要使用Bootstrap的js文件,以及登录页功能实现的js,那么为什么在页面的最后引入,这里涉及到一个看似很常见但是没多少人能说的清楚的问题——性能分析。

  如果读者有兴趣了解前端性能知识,我贴一个链接大家自己看看,放这里细说不合适:

https://blog.csdn.net/ywb201314/article/details/53170298

六 Login.js的实现

  第十部分我贴了自己常写的Js模式,模板以登录页的Js为例,第十部分还没有实现任何逻辑,这部分逐步的将其补充完整。

  其实Js并没有什么很特殊的地方,更多的是Ajax的使用,因为页面没有数据是不完整的,而数据处理都是通过Ajax来和服务端进行交互实现的,所以这部分我主要将请求及应答数据的处理进行一个很简略的介绍。

  以用户注册功能举例:

$(function () {
    LoginPage.initial();
});

var LoginPage = {
    initial: function () {
        LoginPageMVC.View.initial();
    },
    URLs: {
        base: 'http://localhost:8080/JianZi',
        login: {
            // TODO
        },
        register: {
            url: LoginPage.URLs.base + '/user',
            method: 'POST',
            params: {
                'action': 'register',
                'phone': '',
                'password': '',
                'nickname': ''
            }
        }
    }
};

var LoginPageMVC = {
    Model: {
        user: {
            'phone': '',
            'password': '',
            'nickname': '',
            'token': ''
        }
    },
    View: {
        initial: function () {
            $("#inputPhone").val(LoginPageMVC.Model.user.phone);
            $("#inputPassword").val(LoginPageMVC.Model.user.password);
            $("#inputNickname").val(LoginPageMVC.Model.user.nickname);
        },
        refresh: function () {
			// TODO
        }
    },
    Controller: {
        login: function () {
            // TODO
        },
        register: function () {
            LoginPageMVC.Model.user.phone = $("#inputPhone").val();
            LoginPageMVC.Model.user.password = $("#inputPassword").val();
            LoginPageMVC.Model.user.nickname = $("#inputNickname").val();

            LoginPage.URLs.register.params.phone = LoginPageMVC.Model.user.phone;
            LoginPage.URLs.register.params.password = LoginPageMVC.Model.user.password;
            LoginPage.URLs.register.params.nickname = LoginPageMVC.Model.user.nickname;

            $.ajax({
                async: false,
                type: LoginPage.URLs.register.method,
                url: LoginPage.URLs.register.url,
                data: LoginPage.URLs.register.params,
                dataType: 'jsonp',
                success: function (result) {
                    if (result.code != '000000') {
                        alert(result.message);
                        LoginPageMVC.View.initial();
                    } else {
                        alert("注册成功");
                    }
                },
                error: function () {
                    alert("注册请求发送失败");
                }
            });
        }
    }
};

  初始化页面的时候,将model绑定在表单上,而后就是注册方法LoginPage.Controller.register()的实现,一个很简单的Ajax,以Jsonp方式请求,应答则是前半部分的服务端设计中提到的IResponse对象的JSON序列化格式,如果code值为000000则说明请求成功,否则请求失败,如果因网络问题或者其他情况导致的请求发送失败,则通过绑定的error回调函数弹出提示框,提示用户请求发送失败。

七 服务端对请求的处理

  服务端接收到请求后,首先根据请求Url的Servlet映射值确定具体的请求处理Servlet实例,然后通过参数action的值来确定处理该请求的方法:

<servlet>
	<servlet-name>user</servlet-name>
	<servlet-class>com.bubbling.servlet.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>user</servlet-name>
	<url-pattern>/user</url-pattern>
</servlet-mapping>
public class UserServlet extends IServlet {

	private static final long serialVersionUID = 1L;
	private static UserService service = UserService.getService();

	@Override
	protected Map<String, String> getMethodMap() {
		return new HashMap<String, String>() {

			private static final long serialVersionUID = 1L;

			{
				put("register", "register");
				put("login", "login");
			}
		};
	}

	/**
	 * 用户注册请求
	 * <p>
	 * action:register
	 * <p>
	 * 参数:phone(必填)、password(必填)、nickname(选填)
	 */
	public void register() {
		try {
			if (service.register(getParam("phone"), getParam("password"),
					getParam("nickname"))) {
				setWebResponse(Constant.STR_ERROR_CODE_SUCCESS);
			} else {
				setWebResponse(Constant.STR_ERROR_CODE_REGISTER_FAILUER);
			}
		} catch (ServiceException e) {
			setWebResponse(e.getCode());
		}
	}
	……
}

  UserServlet.register()方法通过UserService来处理注册相关的业务逻辑,其中getParam()方法被封装在IServlet中,作为所有IServlet派生类公用的方法,这部分可以查阅之前服务端设计部分。

  这里我对所有请求处理的结果做了一个code码整理,其静态值保存在Constant类中,这部分设计在前面的章节也有提到。并且所有业务层的设计都是通过抛出ServiceException来作为业务处理结果的,其设计如下:

package com.bubbling.common;

/**
 * 公用服务异常,仅供业务层处理使用 <br>
 * 当业务逻辑处理失败,包括参数校验失败、数据访问失败、文件操作失败等各种场景下抛出 <br>
 * 该异常在应答前结束作用域,对应答拼装提供错误码及错误信息
 * 
 * @author 胡楠
 *
 */
public class ServiceException extends Exception
{

	private static final long serialVersionUID = 1L;
	private String code;
	private String info;

	public ServiceException(String code)
	{
		this(code, null);
	}

	public ServiceException(String code, String info)
	{
		this.code = code;
		this.info = info;
	}

	public String getCode()
	{
		return code;
	}

	public void setCode(String code)
	{
		this.code = code;
	}

	public String getInfo()
	{
		return info;
	}

	public void setInfo(String info)
	{
		this.info = info;
	}

	@Override
	public String getMessage()
	{
		return toString();
	}

	@Override
	public String toString()
	{
		return "ServiceException [code=" + code + ", info=" + info + "]";
	}

}

  如果Service处理失败,则抛出ServiceException,异常中包括了错误码code值,和错误信息info值,这样Servlet在得到异常后会根据code值在Constant类中取得相应的应答数据,然后拼装IReponse对象,并响应客户端请求:

/**
 * @author 胡楠
 * 
 *         所有Servlet均需要自该类派生,并且需实现处理请求映射的获取方法,派生类的所有方法访问类型按规范必须声明为public,
 *         且返回值为void
 *
 */
public abstract class IServlet extends HttpServlet
{
	……
	/**
	 * @return kEY值为请求参数action值,VALUE值为处理该请求的方法名
	 */
	protected abstract Map<String, String> getMethodMap();
	……
	private void process() throws IOException
	{
		……
		try
		{
			processAction();
		}
		catch (Throwable t)
		{
			result.setCode(Constant.STR_ERROR_CODE_SYSTEM_ERROR);
			result.setMessage(Constant.MAP_ERROR.get(Constant.STR_ERROR_CODE_SYSTEM_ERROR));
		}
		……
		try
		{
			PrintWriter out = response.getWriter();
			……
			out.flush();
			out.close();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	……

  而Service层不仅需要处理请求参数的校验,还需要通过Dao层与数据库进行交互,如果用户注册成功,需要向用户表插入一条记录,并且向用户会话表插入一条记录,用户会话记录则随着每次用户的登陆、登出等操作进行更新操作:

public boolean register(String phone, String password, String nickname) throws ServiceException {
	verifyPhone(phone);
	verifyPassword(password);
	if (StringUtil.isEmpty(nickname)) {
		nickname = StringUtil.getUUID().substring(0, 6);
	}
	User user = new User();
	user.setPhone(phone);
	user.setPassword(password);
	user.setNickname(nickname);
	return dao.add(user);
}

  这里的verifyPhone()方法不仅需要校验手机号是否为空,还需要对号码格式进行验证,通过简单的正则判断可以实现,这些公用的校验方法我们额外进行封装,以便于重用:

private void verifyPhone(String phone) throws ServiceException {
	if (StringUtil.isEmpty(phone)) {
		throw new ServiceException(Constant.STR_ERROR_CODE_PHONE_EMPTY);
	}
	if (!RegexUtil.IsCellphone(phone)) {
		throw new ServiceException(Constant.STR_ERROR_CODE_PHONE_FORMAT);
	}
}

public static boolean isEmpty(String value) {
	return value == null ? true : "".equals(value.trim());
}

/**
 * 验证手机号码
 * 
 * @param str
 * @return
 */
public static boolean IsCellphone(String str) {
	String regex = "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";
	return match(regex, str);
}

  最后UserService通过UserDao来进行数据库交互,UserDao本身是一个接口定义,在UserService中被UserDaoMysqlImpl实例化,UserDaoMysqlImpl中实现了UserDao的所定义的数据交互方法,这里不细细列举了,因为都是简单的JDBC操作,个人认为将前后端的逻辑串联起来是最重要的,其实现细节毫无意义。

八 结语

  到此为止,我从服务端一路高歌猛进到前端设计,将整个设计流程通过登录页这个案例串联了起来,再往后我觉得已经没有进行介绍的意义了,因为都是重复性的东西。

  这里做一个设计的总结:

  1. 数据模型设计
    数据模型设计是一个项目的根基,所有程序设计都应该是围绕数据模型而展开,没有数据模型则没有产品概念,所有的产品设计最终都是为了那些存于文件、存于数据库中的记录。数据模型设计可通过ER模型图或者其他工具设计,表结构设计时需要对字段类型、存储方式进行预先优化设计,尤其是热点表(经常被访问的数据)、热点数据,如何设置其索引、唯一键等很关键,这对服务端的性能而言极其重要。

  2. 服务端结构设计
    在进行服务设计之初需要大量的基础设施设计,包括共有处理部分(各工具类),或是使用第三方Web框架,或是想我一样自己编写一个简单的Web框架,核心都在于如何对设计进行层次化处理,良好的层次结构对于代码的维护、延展至关重要。服务端设计主要考虑实现如何拆分请求处理、业务处理以及数据交互,所有的实现尽量以接口的方式设计,这是模式化设计的基础。

  3. 前端设计
    前端设计其实是最复杂的,因为这是产品的门面,整体风格、样式这些偏产品设计的理念不细讲,站在程序员的角度来说,仅仅关心如何将请求发送至服务端,如何根据服务端的应答数据来更新页面。

  从前端到服务端的一个完整处理思路进行一个整理:

  1. 客户端初始化Html页
  2. 通过Ajax向服务端发起请求
  3. 服务端通过Servlet处理请求,并通过业务层进行请求的业务逻辑处理
  4. 业务层对请求进行业务相关处理,包括参数校验、业务逻辑实现,并通过数据访问层与数据库进行交互
  5. 数据访问层与DB、文件进行交互,并返回交互结果
  6. 业务层根据处理结果向Servlet汇报应答数据
  7. Servlet将应答数据进行拼装,并响应给客户端
  8. 客户端获取Ajax应答数据将其绑定到页面元素中,进行页面的绘制更新

  整个Demo从设计到实现我花了将近两周,因为平时工作的原因,只能周末找点时间做,勉强及格,虽然只有很简单的一些功能,但是对于一个常规Web项目来说,应有的设计也没差很多,当然达到上线产品级是远远不够的。

  一共十一部分从服务端到前端,实现的代码我并没有贴上很多,因为确实没有什么技术含量。

  后面如果有时间我会慢慢完善它,争取能让它早日部署在阿里的云服务器上。大吼一声:完结!

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
目录 1. 介绍 5 1.1 项目概述 5 1.2 范围 5 1.3 参考 5 2. 用例视图 6 2.1 WAS - SAP R/3 集成用例 6 2.1.1 车辆列表功能 6 2.1.2 车辆订购申请单的创建功能 7 2.1.3 车辆订购申请单查询功能 7 2.1.4 车辆订购申请单的修改功能 7 2.1.5 索赔单的创建 8 2.1.6 数据交换需求 8 2.2 PORTAL集成的用例 8 2.2.1 经销商 Portal 框架 9 2.2.2 车辆销售系统和Portal的整合 9 2.2.3 Nadcon system 和Portal系统的整合 10 2.2.4 车辆销售系统和Nadcon 的整合 10 3. 逻辑视图 10 3.1 兼容性 10 3.2 系统架构 10 3.2.1 逻辑架构 10 3.2.2 Web 应用的包设计 12 3.3 组件设计 - J2EE WEB APPLICATION 13 3.3.1 MVC 框架 – Struts 13 3.3.2 日志 14 3.3.3 BAPI代理结构 15 3.3.4 销售商用户信息组件和安全组件 16 3.3.5 面表现框架 17 3.3.6 车辆列表功能 18 3.3.7 车辆订购请求单创建 24 3.3.8 车辆订购申请单查询列表 32 3.3.9 车辆订购申 请单修改 37 3.3.10 索赔单创建 43 3.3.11 数据交换 50 3.3.12 登录 & 退出 53 4. 数据视图 56 4.1 车辆列一表 57 4.2 车辆订购申请单创建 58 4.3 车辆订购申请单列表 59 4.4 车辆订购申请单修改 60 4.5 索赔单创建 61 5. 实现视图 62 5.1 缓存策略 62 5.2 会话管理 62 5.3 连接管理 62 5.4 集成的需要 62 5.4.1 WAS – SAP 集成 63 5.4.2 单点登陆 63 5.4.3 Vehicle Sale 系统 和 Nadcon的集成 63 6. 部署视图 64 6.1 安装需求 64 6.1.1 服务器的安装 64 6.2 服务支持的考虑 64 6.2.1 安全 64 6.2.2 服务器管理 64 7. 实现环境视图 64 7.1 开发环境 64 7.2 测试环境 64 7.3 生产环境 65 7.3.1 网络 65 7.4 域信息 65

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬睡客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值