其他测试框架 openstf 模块解读--app

blueshark · 发布于 2017年09月27日 · 最后由 jiazurongyu 回复于 2017年09月29日 · 556 次阅读
本帖已被设为精华帖!

一、app模块介绍

  • app模块位于lib/units/app/路径下,app模块的作用是提供一个完整的http服务器,这里的『完整』的意思是指包括html、js、css、image等所有的web静态内容。如果你深入了解过stf前面的实现原理,就会知道stf是一个完全的前后端分离式设计,简单来说,动态的数据完全是从接口来获取的,页面渲染完全由js来完成,app模块的作用就是把前端的所有东西返回给浏览器,返回完以后,前端面就和app模块没有任何交互了!!!如果你不相信,可以在打开stf首页以后把app模块停掉(需要用docker等分离式部署方式),完全不会影响stf的正常使用,除非你刷新页面。

  • app模块从原理上讲非常简单,它就是一个普通的http服务器,使用express来实现,而它所需要返回的东西已经由webpack打包好了。

  • 这里多说一句,如果你在开发stf的时候,用local方式启动,只是对前端做了修改,只需要执行npm install一下然后刷新页面就可以了,不用重新启动整个stf。

二、app模块的启动和代码解读

2.1 app模块的启动方式

启动方式可以参考文档中给出的docker启动命令,下面只摘出主要的部分:

stf app --port 3000 \
  --auth-url https://stf.example.org/auth/mock/ \
  --websocket-url wss://stf.example.org/

也就是说,只要指定三个参数port、授权--auth-url、websocket地址--websocket-url即可,auth和websocket的参数会在启动对应的模块时指定。这里的auth参数的作用是在app模块中授权失败的时候自动跳转么授权(登录)页面,而websocket的url似乎只是在GLOBAL_APPSTATE中用到,没有用来建立连接。

这里面的"stf"这个命令可以执行是因为我们把bin/stf这个文件加入到了系统环境变量,打开bin/stf这个文件可以看出只有一行../lib/cli/please,然后我们看看这个please是什么,发现它其实指向了../lib/cli/index.js,因此,你如果没有把bin/stf加入到系统环境变量,直接用node ../lib/cli/index.js也可以,或者直接 node ../lib/cli,例如local启动可以直接

node ../lib/cli local --public-ip xxx

或者这样启动app

node ../lib/cli app --port 3000 \
  --auth-url https://stf.example.org/auth/mock/ \
  --websocket-url wss://stf.example.org/

2.2 代码解读

首先看index.js文件,这是app模块的入口。在app模块中引入了不少其他的模块,下面简单说一下,其实如果你了解过express框架,这些都很简单了。

  • http:node的表态网页服务器,最新版的express似乎不用这个来创建服务器了。
  • url:用来解析url的一个工具,由nodejs直接提供。其中url.parse可以将url字符串转换为对象。
  • fs:文件系统,在这里用来判断某个文件(夹)是否存在。
  • express:不用说了,用来创建服务器的。
  • express-validator:express的验证器,主要用来验证用户的输入是否合法,这里还没看出有什么用。
  • cookie-session:是一个基于cookies的session中间件。
  • body-parser:用来解析expess的body参数的中间件,如果你的http写了Content-Type为application/json,那么就需要bodyParser.json()来解析。
  • serve-favicon:这个是处理网站logo的一个中间件,由于logo被经常访问,serve-favicon可以加快logo的访问速度,stf的logo文件是STF-128.png。
  • serve-static:处理express表态资源的一个中间件,允许指定服务器的某个路径为本机的某个静态资源文件夹。
  • csrf:这是一个防止跨站点伪造请求的中间件,可以防止别人利用cookies来攻击你的网站,具体可以搜索CSRF的攻击与防御。
  • compression:提供web的压缩功能,比如说gzip等。
  • logger:打出对应模块的log,会在其他文章中详细解释这个logger的用途。
  • pathutil:路径一个工具,主要用途是给对应的目录加上对应的路径,这个在后面的文章中再做详细介绍。
  • auth:这是一个控制授权的工具,下面再详细介绍。
  • deviceIconMiddleware、browserIconMiddleware、appstoreIconMiddleware处理对应icon的中间件,其实就是把对应的icon的路径指到对应的node_module中。
  • markdownServe:一个用来展示markdown文件的工具,在主页面的wiki中会用到。

app.set是设备服务器的一些参数:

  • pug:express服务器的模板引擎为pug,了解exress的同学都知道express其实支持很多种模板的。
  • views:指express web视图的路径。
  • strict routing:这个是http的一个模式,我也没深入了解。
  • case sensitive routing:是否区分路径中的大小写。
  • trust proxy:信任代理?比如用nginx做了代理,那么后端应用就不能直接获取到用户的ip,这时候可以通过「X-Forwarded-」来获取用户真实的ip,不过这个很容易伪造。

app.use(路径,function(){})就是处理对应路径的一些方法了,比如说/static/wiki、/static/app/build/entry、/static/app/data等,假如用户请求的是/static/logo这个路径,那么express会把请求交给serveStatic这个方法来处理。详情可以查询一下express的中间件相关知识。

看下面这一段:

if (fs.existsSync(pathutil.resource('build'))) {
  log.info('Using pre-built resources')
  app.use(compression())
  app.use('/static/app/build/entry',
    serveStatic(pathutil.resource('build/entry')))
  app.use('/static/app/build', serveStatic(pathutil.resource('build'), {
    maxAge: '10d'
  }))
}
else {
  log.info('Using webpack')
  // Keep webpack-related requires here, as our prebuilt package won't
  // have them at all.
  var webpackServerConfig = require('./../../../webpack.config').webpackServer
  app.use('/static/app/build',
    require('./middleware/webpack')(webpackServerConfig))
}

这段话的意思是如果存在build文件夹(已经用webpack build过),那么就使用build文件夹中的内容,否则就要使用webpack热生成了。关于webpack中间件下文再做介绍。

app.use(cookieSession({
  name: options.ssid
, keys: [options.secret]
}))

app.use(auth({
  secret: options.secret
, authUrl: options.authUrl
}))

cookieSession需要设置name和keys两个参数,key是用来对cookies进行签名和验证用的。

auth方法也需要传入两个参数secret和authUrl,authUrl是指授权url,在授权模块中会指定。

下面看一下middleware文件夹中的auth文件,在这里定义了auth方法。先看第一段话:

if (req.query.jwt) {
  // Coming from auth client
  var data = jwtutil.decode(req.query.jwt, options.secret)
  var redir = urlutil.removeParam(req.url, 'jwt')
  if (data) {
    // Redirect once to get rid of the token
    dbapi.saveUserAfterLogin({
        name: data.name
      , email: data.email
      , ip: req.ip
      })
      .then(function() {
        req.session.jwt = data
        res.redirect(redir)
      })
      .catch(next)
  }
  else {
    // Invalid token, forward to auth client
    res.redirect(options.authUrl)
  }
}

if (req.query.jwt)是指query中包含jwt的字段,query中的jwt字段是指url中直接包含jwt=xxxx等内容,在用户每一次用mock方式登录的时候会出现这种情况。下面就是把jwt中的内容解析为明文信息data,然后是解析jwt中的重定向url到redir。如果发现jwt解析成功,就把对应的用户存储在数据库中,如果解析不成功,就重定向到授权的url。

总结一下,这段代码其实是处理用户第一次登录的时候的验证问题,用户第一次登录的时候由于cookies没有jwt token,只能由auth模块在url的后面加入jwt参数来授权,然后由app模块解析。

else if (req.session && req.session.jwt) {
  dbapi.loadUser(req.session.jwt.email)
    .then(function(user) {
      if (user) {
        // Continue existing session
        req.user = user
        next()
      }
      else {
        // We no longer have the user in the database
        res.redirect(options.authUrl)
      }
    })
    .catch(next)
}

在else语句中,是从session中解析了用户信息。一个典型的jwt串如下:

{ jwt: 'eyJhbGciOiJIUzI1NiIsImV4cCI6MTUwMTQxMTAzNzg5NH0.eyJlbWFpbCI6InRlc3R1c2VyQHRlc3QuY29tIiwibmFtZSI6InRlc3R1c2VyIn0.W5zYDcA4wu6kB1GWR9BLOKdGtyDwRO9IQaA2LqW7CrY' }

下面这段代码:

app.all('/app/api/v1/dummy', function(req, res) {
  res.send('OK')
})

我也没搞清楚是干什么用的,估计是测试用的。

下面的bodyParser、csrf、validator可以参数对应的中间件。res.cookie是把XSRF-TOKEN写入cookies,然后在请求的时候带上,防止伪造请求。

app.get('/', function(req, res) {
  res.render('index')
})

这段就是用户访问stf的根目录时处理的代码了,这里返回了index,是指res/app/views/index.pug这个文件。

app.get('/app/api/v1/state.js', function(req, res) {
    ...xxxx
})

这段是提供app状态的代码,它的后缀有点儿奇怪,是.js,当别人访问它的时候,会认为他是一个js文件,但是不真的是js文件,而且在response里设置type为'application/javascript',应该是为了动态设置前端某个静态变量用的,等我研究透了STF前端再详细介绍。在res/app/views/index.pug有用到这个路径。

2.3 app模块的访问路径

app模块的访问路径就是STF网站的根目录。下面是app模块的nginx配置:

location / {
  proxy_pass http://stf_app;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Real-IP $http_x_real_ip;
}

可以看出app的访问路径是/。

三、app模块简单总结

  • 当我们执行stf app xxx的时候,yargs这个命令行工具会执行lib/units/app/index.js这个文件,然后会启动一个express服务器。如果用户访问stf网站的根目录,express服务器会返回webpack打包好的html、css、js、img等文件,其中js会建立websocket或者请求api与后端交互。在app中也包含了cookies和授权等内容。

  • app本身其实并不复杂,而它向浏览器传输的内容--前端框架则是STF中非常复杂的一部分。

共收到 2 条回复
104 seveniruby 将本帖设为了精华贴 09月28日 10:42
104

不错 这样的工具解读可以多来点

899

写得不错。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册