页面程序流程
页面程序,指与页面相关的程序逻辑代码。
页面程序流程,指带有一定执行顺序的页面程序逻辑代码。
- 页面的程序流程有哪些?
- 页面的程序流程的执行顺序是怎样的?
try{
if(Check)
{
会话期限 else { 记录操作 return }
用户登录 else { 记录操作 return }
功能权限 else { 记录操作 return }
用户权限 else { 记录操作 return }
}
界面风格
业务逻辑(Page_Load/界面操作事件)
语言
记录操作
} catch(e){
记录错误
显示错误
复原窗口
}
- 每个程序流程在页面打开事件中是否执行?
- 每个程序流程在界面操作事件中是否执行?
页面打开事件,指页面打开时执行的逻辑程序,如WebForm的Page_Load、微信小程序的OnLoad
界面操作事件,指如按钮的点击事件、文本框的更改事件等
程序流程 | 页面打开事件 | 前后端分离 的 界面操作事件 |
WebForm 的 界面操作事件 |
---|---|---|---|
会话期限 | 执行 | 执行 | 执行 |
用户登录 | 执行 | 执行 | 执行 |
功能权限 | 执行 | 执行 | 执行 |
用户权限 | 执行 | 执行 | 执行 |
界面风格 | 执行 | 不需要执行 | 实际上执行 |
业务逻辑 | 执行 | 执行 | 执行 |
语言 | 执行 | 可能执行 | 实际上执行 |
记录操作 | 执行 | 执行 | 执行 |
记录错误 | 执行 | 执行 | 执行 |
显示错误 | 执行 | 执行 | 执行 |
复原窗口 | 执行 | 执行 | 执行 |
前后端分离指类似安卓app、苹果app、微信小程序、Vue的开发模式,即前端通过ajax无刷新更新的方式加载数据。
WebForm指asp.net的aspx后缀的页面开发模式,即开发模式主要依赖于from,每个事件会把form里的所有数据通过form提交到后端。
会话期限与用户登录
【会话】与【用户】的关系:
- 一个【会话】只能有一个【用户】,表示登录的用户,如果未登录用户,则【会话】中的【用户】相关信息为null。
- 一个【用户】可以有多个【会话】,因为用户可以登录在多个设备上、多个浏览器上、多个使用环境上、以及浏览器的多个选项卡上。
登录用户的程序流程:
- 验证用户账号和密码是否正确
- 把【用户】的相关信息记录在【会话】中
- 当登录的【用户】已有登录在多个地方的【会话】,则提示用户是否退出该用户正在使用的其它地方的【会话】,并提醒该用户其它地方的使用者:“有其它地方已登录你的账号”。
在原框架中,同一个用户不能同时登录多个使用端、或多个浏览器、或多个浏览器上的选项卡。
在新框架中,不再做同时登录同一用户的限制。
主动登录用户的操作流程:
- 未登录情况下
- 打开管理界面(Main.aspx)
- 点击右上角登录按钮
- 弹出登录窗口
- 录入账号密码
- 点击登录
- 关闭登录窗口,并提示登录成功
在原框架中,未登录情况下,是不能直接打开主界面(Main.aspx)的,只能先打开登录界面(Login.aspx)。
在原框架中,登录入口有两种,分别在登录页面(Login.aspx)、主界面(Main.aspx)使用过程中要求重新登录时。
而在新框架中,取消了使用登录页面的入口(Login.aspx),保留了主界面的登录入口,这样做的好处是:
- 有些功能可能不需要要求用户登录,这样在主界面未登录情况下直接打开即可。如:新闻公告、活动公告、节假日公告等
- 统一登录界面的入口,使登录入口只保留一种入口。
打开功能时要求登录的操作流程:
- 未登录情况下
- 打开管理界面(Main.aspx)
- 点击菜单栏的功能菜单项
- 弹出指定功能的窗口与页面界面,如果该功能需要登录,则界面只显示布局界面,不显示数据。
- 如果该功能需要登录,再弹出一个登录用户的窗口。
- 录入账号密码
- 点击登录
- 关闭登录窗口,并提示登录成功
- 该功能界面重新加载数据
会话到期时要求登录的操作流程:
- 会话到期情况下
- 当打开页面或操作界面
- 如果打开页面的功能需要登录用户,则界面只显示布局界面,不显示数据;如果操作界面的功能需要登录用户,则保持操作前的界面和数据状态。
- 再弹出一个登录用户的窗口
- 录入账号密码
- 点击登录
- 关闭登录窗口,并提示登录成功
- 如果是打开页面要求登录的,则打开的页面重新加载数据;如果是操作界面要求登录的,不会自动重新操作,需要用户自行重新操作。
在原框架中,会话到期,会关闭打开中的界面、或操作中的界面,这样用户原来操作的其它过滤操作、打开操作、选项卡等操作,就需要重新打开页面,并重新操作一遍过滤、选项卡等操作。
保持会话状态:
- IIS重启后,用户不应该要重新登录,如果用户正在操作中并提醒“系统中断操作失败”。
- 操作系统重启后,用户不应该要重新登录,如果用户正在操作中并提醒“系统中断操作失败”。
- 系统崩溃后,用户不应该要重新登录,如果用户正在操作中并提醒“系统中断操作失败”。
在原框架中,系统可能因为不确定的原因,导致系统不间断重启,导致用户经常需要重新登录。
程序设计应该多往程序有缺陷的情况下去考虑设计,有时程序很难做完美没有任何问题。
会话期限、用户登录与App.Page.Check属性的设置关系:
- 如果Check的值为None,不检查会话期限、用户登录,即界面操作不需要用户登录。
- 如果Check的值为All,需要检查会话期限、用户登录,即界面操作需要用户登录。
App.Page.Check属性的详细说明请看本文章节“App.Page.Check属性”
记录错误与显示错误
指页面程序流程中,程序出错了把错误信息记录下,并提示用户程序出错。
指页面程序流程中,程序出错时:
- 把错误信息记录下来。
- 另外弹出窗口,提示用户程序出错,不应该显示错误的详细信息,只需要显示错误信息记录的Id,以便于开发人员直接找到错误信息。
- 出错的页面应该保留出错前的样子,不应该关闭页面。
在原框架中,出错时只显示出错了,要自己去找这次错误对应的大概时间的那条错误记录。
在原框架中,出错时会显示错误的详细信息,会让普通用户去看一大串看不懂的详细信息,那是只有专业的开发人员才看懂,而且这样会提高用户的出错心理阴影。
在原框架中,如果出错了原页面会关闭,这样用户原来操作的其它过滤操作、打开操作、选项卡等操作,就需要重新打开页面,并重新操作一遍过滤、选项卡等操作。
记录操作
- 记录哪个用户在什么时间点打开哪个页面。
- 记录哪个用户在什么时间点在界面上操作了什么。
- 记录操作有没有执行主业务逻辑的程序流程,如果执行那大概执到哪个流程中断了。
如果用户还没登录,也需要记录操作,这样可以了解用户没登录的情况下操作了什么,记录操作应该更全面,有助于调查操作信息更可靠。
如果客户没有购买功能,也需要记录操作,这样可以了解客户是否对某个功能感兴趣常常去点点看,了解客户的好奇行为信息,并且记录操作应该更全面,有助于调查操作信息更可靠。
如果用户没有打开页面的权限,也需要记录操作,记录操作应该更全面,有助于调查操作信息更可靠。
如果用户没有某项界面操作的权限,也需要记录操作,记录操作应该更全面,有助于调查操作信息更可靠。
功能权限与用户权限
功能未激活:
- 打开页面
- 如果页面的功能未激活,则关闭当前页面
- 提示用户功能未购买激活,或未来可考虑设计成显示购买界面。
打开页面时,功能未授权:
- 打开页面
- 如果用户没有功能的查看权限,则关闭当前页面
- 提示用户功能未授权。
操作界面时,功能未授权:
- 操作界面
- 如果用户有功能的操作权限,则取消执行界面操作的业务逻辑程序
- 提示用户功能未授权。
功能权限、用户权限、App.Page.Check属性、App.Page.FunctionId的设置关系:
- 如果Check的值为None时,不检查功能权限、用户权限。
- 如果Check的值为All时,需要检查功能权限、用户权限。
App.Page.Check属性的详细说明请看本文章节“App.Page.Check属性”
App.Page.FunctionId属性的详细说明请看本文章节“App.Page.FunctionId属性”
语言
语言,如指软件的英文版、繁体版、简体版等。
判断使用哪个语言的程序逻辑:
- 如果会话在有效期内,则读取会话记录中的语言数据。
- 如果读取会话记录中的语言数据为null,则读取会话记录中的语言数据。
- 如果会话已超时,则读取客户端记录中的语言数据。
- 如果读取客户端记录中的语言数据为null,则使用默认语言。
- 如果会话记录不存在,则读取客户端记录中的语言数据。
- 如果客户端记录不存在,则提示“刚刚开发人员调试初始化客户端,操作被取消了,请重新操作。”,并初始化客户端记录。
新框架语言与原框架语言的区别:
- 语言程序流程是优先于主要业务逻辑的执行,如果业务逻辑中有动态控件的生成,那么优先执行的语言程序流程就无法涉及到动态控件的语言控制了。因此,新框架中,语言程序流程放在了主业务逻辑的后面执行。
界面风格
界面风格,如指软件的蓝色风格、黑色风格、白色风格等。
判断使用哪个界面风格的程序逻辑:
- 如果会话在有效期内,则读取会话记录中的界面风格数据。
- 如果读取会话记录中的界面风格数据为null,则读取会话记录中的界面风格数据。
- 如果会话已超时,则读取客户端记录中的界面风格数据。
- 如果读取客户端记录中的界面风格数据为null,则使用默认界面风格。
- 如果会话记录不存在,则读取客户端记录中的界面风格数据。
- 如果客户端记录不存在,则提示“刚刚开发人员调试初始化客户端,操作被取消了,请重新操作。”,并初始化客户端记录。
处理进度
- 显示处理进度条时,用户还可以操作其它界面处理其它事情。
- 可以同时处理多个事情,并显示多个处理进度条信息。
- 用户通过进度条可以主动中断处理。
- 用户可以看到公共处理进度信息,处定时的收集卡机数据的进度、自动化分析考勤的进度信息等。
- 用户关闭浏览器后,不需要中断处理进度。
- 用户重新登录后,如果处理进度还在进行,则可以继续看到处理进度条。
- 用户可以看到其它用户的处理进度情况,以便了解服务器的繁忙情况。
- 通过ProgressEntityService统一管理处理进度信息,有利于更好的实现管理处理进度功能。
新框架中,不再通过window.Progress = new Progress(……),来控制进度条信息,将统一使用ProgressEntityService代替。。
异步
- 异步可以同时更新数据,而不必一条条更新语句按顺序执行。
- 异步可以同时查数据,而不必一条条查询语句按顺序执行。
- 异步可以在等待硬盘IO操作的同时,处理其它事情。
- 异步不是多线程,多个异步操作同一个变量不存在数据计算偏差和线程安全问题,但多个线程操作同一个变量存在计算数据偏差和线程安全。
- .aspx代码文件,需要在顶部的<%@Page %>中加上Async="true"设置,指示该页面允许使用异步。
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Test.aspx.cs" Inherits="Test" Async="true" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
- .aspx.cs代码文件,所有逻辑代码需要写在RegisterAsyncTask里。
using System;
using System.Web.UI;
public partial class Test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(async () =>
{
//逻辑代码
}));
}
protected void OnMyEvent()
{
RegisterAsyncTask(new PageAsyncTask(async () =>
{
//逻辑代码
}));
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
RegisterAsyncTask(new PageAsyncTask(async () =>
{
//逻辑代码
}));
}
}
App.Page.ClientId属性
- 表示当前使用端是哪个【客户端】Id。可以通过ClientId获取到【客户端】的地理位置、IP地址、网卡物理地址、使用语言、界面风格……等
- ClientId的有效期是2小时。
- 用户端在线状态时,每间隔1小时,由客户端自行刷新有效期。
- ClientId使用时,不需要判断有效期是否在2小时内。
- ClientId可由【客户端】自行创建,也可由【服务端】创建。
- ClientId在有效期内时:
- 重新打开浏览器时,保持ClientId
- 刷新浏览器时,保持ClientId
- 重启服务器时,保持ClientId
- 使用时,保留ClientId
- ClientId超过有效期时:
- 重新打开浏览器时,重新创建ClientId
- 刷新浏览器时,保持ClientId
- 重启服务器时,保持ClientId
- 使用时,保留ClientId
有效期状态 | 重新打开浏览器 | 刷新浏览器 | 重启服务器 | 使用时 |
---|---|---|---|---|
在有效期内 | 保持 | 保持 | 保持 | 保持 |
超过有效期 | 重新创建 | 保持 | 保持 | 保持 |
App.Page.SessionId属性
- 表示当前使用者的【会话】Id,可以通过SessionId获取到:登录用户Id、登录用户名、客户端Id、客户端信息……等
- SessionId的有效期是2小时。
- 用户端在线状态时,每间隔1小时,由客户端发送请求到服务端,自动刷新有效期。
- SessionId使用时,需要判断有效期是否在2小时内。
- SessionId由【服务端】创建。
- SessionId在有效期内时:
- 重新打开浏览器时,保持SessionId
- 刷新浏览器时,保持SessionId
- 重启服务器时,保持SessionId
- 使用时,保持SessionId
- SessionId超过有效期时:
- 重新打开浏览器时,重新创建SessionId
- 刷新浏览器时,重新创建SessionId
- 重启服务器时,重新创建SessionId
- 使用时,重新创建SessionId
有效期状态 | 重新打开浏览器 | 刷新浏览器 | 重启服务器 | 使用时 |
---|---|---|---|---|
在有效期内 | 保持 | 保持 | 保持 | 保持 |
超过有效期 | 重新创建 | 重新创建 | 重新创建 | 重新创建 |
App.Page.FunctionId属性的使用说明
- 表示该【页面】只属于一个【功能】
- 如果App.Page.Check的值包含Function,并且FunctionId有值时,则页面会检查功能权限。
- 如果App.Page.Check的值包含Function | UserSession,并且FunctionId有值时,则页面会检查功能权限和用户权限。
- 如果App.Page.Check的值包含Function时,FunctionId必需要有值,否则会报错“FunctionId is required for checking”
- 如果App.Page.Check的值不包含Function时,即使FunctionId有值,不会检查功能权限和用户权限。
检查项 | Function | SessionUser |
---|---|---|
功能权限 | √ | - |
用户权限 | √ | √ |
一个【页面】也可以属于多个【功能】,就需要其它处理方法了,可以把Check设置None,自行实现检查逻辑,就可以实现多个【功能】的检查。
FunctionId相当于在原框架中的GetWindowId()方法,区别和优点:
- 区别1:GetWindowId是一个方法,FunctionId是一个属性。
- 区别2:GetWindowId方法的返回类型是整型,FunctionId的属性类型是一个分布式Id
- 区别3:GetWindowId不为null时,需要检查功能权限和用户权限。FunctionId不为null时,不代表需要检查功能权限和用户权限。
- 优点1:未来考虑多方合作商共同参与开发,如果功能Id采用数字可能会常常发生重复冲突,需要中心化集中维护功能Id。如果果采用分布式Id,各自合作商自行生成Id即可,互不干扰,去中心化各自维护功能Id。
- 优点2:去中心化同样方便于公司内部开发人员使用,内部开发人员自行生成Id即可,互不干扰,去中心化各自维护功能Id。
代码示例:
public class MyPage : App.Page
{
protected internal override Id<FunctionEntity> FunctionId => new Id<FunctionEntity>("60244fff6545947758630631");
}
App.Page.WindowId属性
- 指窗口的id
- 一个窗口会有一个访问地址(Src)
- 一个窗口可以最大化、最小化
- 一个窗口可以显示坐标
- WindowId必需要有值,如果WindowId为null,则会报错提示“WindowId异常,需要联系软件工程师处理”。
App.Page.Check属性
- Check的值为All时,表示页面是否需要检查:会话期限、用户登录、功能权限、用户权限
- Check的值为Function时,表示页面只做功能权限检查
- Check的值为Session时,表示页面只做会话期限检查
- Check的值为SessionUser时:
- 检查会话期限
- 检查用户登录
- Check的值为Function | SessionUser时:
- 检查会话期限
- 检查用户登录
- 检查功能权限
- 检查用户权限
- Check的值为None时,表示页面不做检查。
- Check的值可以为组合值,如:Function | Session
- Check的默认值为None
- 如果Check的值为Function时,App.Page.FunctionId必需要有值,否则会报错“FunctionId is required for checking”
检查项 | Function | Session | SessionUser |
---|---|---|---|
会话期限 | - | √ | - |
用户登录 | - | - | √ |
功能权限 | √ | - | - |
用户权限 | √ | - | √ |
如果需要自行编写检查逻辑,则把Check设置为None。
.Net WebForm的内置接口
.Net WebForm的内置接口,指 .net自带的接口。
在新框架中,以下内置接口保持使用:
- Page_Load和OnLoad,用于处理页面数据的加载。
- IsPostBack,用于表示页面是否处于事件逻辑。(目前暂时使用,后续应该会改动,以更适合未来的前后端分离的开发模式)
- 控件的事件方法,如:myBtn_OnClick(object sender, EventArgs e)
- OnPreRender,用于设置控件的非持久状态的数据。