在上一篇文章,完成了实体的定义,接下来要做的是定义应用服务,以便提供api接口。不过,为了更好的结合客户端,笔者决定暂时把这个工作缓一下,和客户端的改造一同进行。
新建一个名为SimpleCmsWithAbp-Client的文件夹,把SimpleCMS的Ext JS脚本复制到文件夹中。然后用Visual Studio Code打开该文件夹。使用Visual Studio Code是因为在这里不需要编写C#代码,使用vscode比使用Visual Studio方便很多。
打开文件夹后,先为vscode安装一个名为IIS Express的扩展(warren-buckley.iis-express)用来启动IIS Express。扩展安装完成后,就可在打开的文件中单击鼠标右键,在菜单中选择命令面板,就可在如下图所示的输入框中执行以下命令来启动、重新启动或停止IIS Express了:
由于ABP框架模版默认是使用4200端口来作为跨域访问的客户端网站端口的,因而,我们需要将当前文件夹的访问端口设置为4200。在当前文件夹下新建一个名为.vscode
的文件夹,然后在文件夹下创建一个名为iisexpress.json
的文件,并在里面添加以下代码:
{
"port":4200
}
好了,现在可以启动IIS Express来访问网站了,不过执行肯定是错误的。我们先打开app.json文件,将里面的首页文件(indexHtmlPath
)设置回index.html
。然后打开index.html文件,在调用bootstrap.js
脚本的SCRIPT标记前添加以下代码:
<script>
var ROOTPATH = 'http://localhost:21021';
var LOCALPATH = 'http://localhost:4200';
script>
以上代码用于指定远程访问地址和本地地址。
接下来打开app\util\Url.js
,将getResource方法修改为以下代码:
getResource: function (res) {
var me = this;
return LOCALPATH + '/' + me.resources[res];
}
代码完成后按CTRL+打开终端窗口执行一次
sencha app build`,重建生成一次项目。现在在浏览器访问应用,会正常显示界面了,不过会提示访问错误。问题不大,接下来就是要修正这些问题。
WebApi是通过令牌认证(Token Auth)来访问的,需要在Ajax的头部添加Authorization来发送认证令牌。在修改Ajax的头部前,需要先考虑这令牌如何保存的问题。根据《Where to Store your JWTs – Cookies vs HTML5 Web Storage》一文,建议的方法是将令牌保存在Cookies中,但这又带来一个CORS的安全问题,需要为Ajax开启cors保护。
在研究了ABP框架模版自带的angular版本客户端的启动过程(AppPreBootstrap.ts
),会发现在提交请求时,会在Ajax的头部添加Authorization
、.AspNetCore.Culture
和Abp.TenantId
这三个头。为了便于在Ajax请求添加这三个头,我们可在app\util
文件夹下,新建一个名为Headers.js
的脚本文件,然后添加以下代码:
Ext.define('SimpleCMS.util.Headers',{
alternateClassName: 'HEADERS',
singleton: true,
requires:[
'Ext.util.Cookies'
],
authTokenCookieName : 'Abp.AuthToken',
authTokenHeaderName : 'Authorization',
tenantIdCookieName : 'Abp.TenantId',
tenantIdHeaderName: 'Abp.TenantId',
cultureTCookieName: 'Abp.Localization.CultureName',
cultureHeaderName: '.AspNetCore.Culture',
getAuthToken: function(){
return Ext.util.Cookies.get(this.authTokenCookieName);
},
getTenantId: function(){
return Ext.util.Cookies.get(this.tenantIdCookieName);
},
getCulture: function(){
return Ext.util.Cookies.get(this.cultureTCookieName);
},
setCookies: function(name, value , expires , path , domain , secure ){
Ext.util.Cookies.set(name, value , expires , path , domain , secure);
},
getHeaders: function(){
var me = this;
return {
'Authorization' : 'Bearer ' + me.getAuthToken(),
'Abp.TenantId' : me.getTenantId(),
'.AspNetCore.Culture' : me.getCulture()
}
}
});
方法getHeaders用户返回组合好的头部,还有3个get方法用于从Cookies中返回令牌、当前用户的默认语言和组合编号,setCookies方法则用于存储Cookies。添加Header类后,在app.js中添加对它的引用。
为了方便的将请求头自动写入Ajax请求的头部,而不需要每次写请求时才加入,最好的方式是重写Ext.Ajax类或在发送请求前统一处理。由于Ext.Ajax是单例模式的类,不便于重写,因而只有在发送请求前统一处理了。经过查看API,发现Ext.Ajax有beforerequest
事件,正是所需的。接下来要考虑的是在哪里执行了。经过测试,发现在Application.js
的launch
方法是不行,因为这时候主视图已经初始化并开始执行流程了。通过查阅Ext.app.Application的API,发现还有一个init方法,它会在主视图初始化之前执行,正是所需的。在init方法中添加以下代码:
init: function(){
Ext.util.Format.defaultValue = function (value, defaultValue) {
return Ext.isEmpty(value) ? defaultValue : value;
}
Ext.Ajax.on('beforerequest', this.onAjaxBeforeRequest, this);
Ext.Ajax.cors = true;
},
在代码中,通过on方法将beforerequest事件绑定到了onAjaxBeforeRequest方法。将cors设置为true是打开Ajax的CORS防御机制。
方法onAjaxBeforeRequest
的代码如下:
onAjaxBeforeRequest: function(conn , options , eOpts ){
if(!options.headers) options.headers = {}
Ext.apply( options.headers, HEADERS.getHeaders());
options.jsonData = true;
}
代码先判断Ajax的选项是否已包含headers的配置,如果没有,则将headers设置为一个对象,然后调用getHeaders方法将返回的头部信息通过apply
方法复制到headers中。将jsonData设置为true是为了将提交的contentType设置为application/json
,而不是默认的text/plain
。
由于使用的是令牌认证,因而,只要验证一下Cookies中是否存在令牌,就可知道用户是否已经登录,如果没有,则显示登录页,如果有,则去获取用户信息。打开app\view\main\MainController.js文件,在onMainViewRender方法中,将代码修改为以下代码:
onMainViewRender: function () {
var me = this,
token = HEADERS.getAuthToken();
if (Ext.isEmpty(token)) {
me.setCurrentView("login");
return;
}
},
好了,现在可以显示登录视图了。总的感觉来说,这比原来的方式简单不少。