commit 878d9b63f64c96b445805c9774fd4f8df461dea7 Author: 轶哥 Date: Mon Jan 23 01:12:24 2017 +0800 v0.0.1 diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..51a8ae4 --- /dev/null +++ b/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["es2015", "stage-2"], + "plugins": ["transform-runtime"] +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9d08a1a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..34af377 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +build/*.js +config/*.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..934f7f2 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,25 @@ +module.exports = { + root: true, + parserOptions: { + ecmaVersion: 6, //指定ECMAScript支持的版本,6为ES6 + sourceType: 'module' + }, + extends: 'standard', + plugins: [ + 'html', + "promise" + ], + env: { + "node": true + }, + rules: { // add your custom rules here + // allow console + "no-console": 0, + // allow paren-less arrow functions + 'arrow-parens': 0, + // allow async-await + 'generator-star-spacing': 0, + // allow debugger during development + 'no-debugger': 0 + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..faf3925 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log +test/unit/coverage +test/e2e/reports +selenium-debug.log +.idea/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..930fc78 --- /dev/null +++ b/README.md @@ -0,0 +1,396 @@ +# Koa2 RESTful API 服务器脚手架 + +这是一个基于Koa2的轻量级Resultful API Server脚手架,支持ES6。 + +约定使用JSON格式传输数据,POST、PUT、DELET方法支持的Content-Type为application/x-www-form-urlencoded和multipart/form-data、application/json,可配置支持跨域。非上传文件推荐application/x-www-form-urlencoded。通常情况下返回application/json格式的JSON数据。 + +可选用mongodb、redis非关系型数据库和PostgreSQL, MySQL, MariaDB, SQLite, MSSQL关系型数据库,考虑RESTful API Server的实际开发需要,这里通过sequelize.js作为ORM,同时提通过Promise执行SQL直接操作Mysql数据库的方法(不管什么方法,注意安全哦)。 + +此脚手架只安装了一些和Koa2不冲突的搭建RESTful API Server的必要插件,附带每一个插件的说明。采用ESlint进行语法检查。 + +因此脚手架主要提供RESTful API,故暂时不考虑前端静态资源处理。基本目录结构与vue-cli保持一致,可配合React、AngularJS、Vue.js等前端框架使用。在Cordova/PhoneGap中使用时需要开启跨域功能。 + +**免责声明:** 此脚手架仅为方便开发提供基础环境,任何人或组织均可随意克隆使用,使用引入的框架需遵循原作者规定的相关协议(框架列表及来源地址在下方)。采用此脚手架产生的任何后果请自行承担,本人不对此脚手架负任何法律责任,使用即代表同意此条。 + +目前暂未加入软件测试模块,下一个版本会加入该功能并提供集成方案。 + +China大陆用户请自行优化网络。 + +## 开发使用说明 +``` +$ git clone https://github.com/yi-ge/koa2-API-scaffold.git + +$ cd mv koa2-API-scaffold +$ npm install +$ npm run dev #可执行npm start跳过ESlint检查。 +``` +访问: http://127.0.0.1:3000/ + +另外一种方式,如果你使用git仓库管理你的代码: + +``` +$ git clone https://github.com/yi-ge/koa2-API-scaffold.git + +$ mv koa2-API-scaffold `您的项目名称` +$ cd `您的项目名称` +$ rm -rf .git +$ git init +$ git remote add origin `您的git仓库地址` +$ npm install +$ npm run dev #可执行npm start跳过ESlint检查。 +``` + +## 调试说明 + +``` +$ npm start --debug + +Or + +$ npm start --debug +``` + +支持Node.js原生调试功能:https://nodejs.org/api/debugger.html + +## 开发环境部署 + +生成node直接可以执行的代码到dist目录: +``` +$ npm run build +``` + +``` +$ npm run production # 生产模式运行 + +Or + +$ node dist/app.js +``` + +### PM2部署说明 +提供了PM2部署RESTful API Server的示例配置,位于“pm2.js”文件中。 +``` +$ pm2 start pm2.js +``` + +PM2配合Docker部署说明: +http://pm2.keymetrics.io/docs/usage/docker-pm2-nodejs/ + +### Docker部署说明 +``` +$ docker pull node +$ docker run -itd --name RESTfulAPI -v "$PWD":/usr/src/app -w /usr/src/app node node ./dist/app.js +``` + +通过 +``` +$ docker ps +``` +查看是否运行成功及运行状态 + +### Linux/Mac 直接后台运行生产环境代码 +有时候为了简单,我们也这样做: +``` +$ nohup node ./dist/app.js > logs/out.log & +``` + +查看运行状态(如果有"node app.js"出现则说明正在后台运行): +``` +$ ps aux|grep app.js +``` + +查看运行日志 +``` +$ cat logs/out.log +``` + +监控运行状态 +``` +$ tail -f logs/out.log +``` + +### 配合Vue-cli部署说明 +Vue-cli(Vue2)运行"npm run build"后会在"dist"目录中生成所有静态资源文件。推荐使用Nginx处理静态资源以达最佳利用效果,然后通过上述任意一种方法部署RESTful API服务器。前后端是完全分离的,请注意Koa2 RESTful API Server项目中config/main.json里面的跨域配置。 + +推荐的Nginx配置文件: +``` +server + { + listen 80; + listen [::]:80; + server_name abc.com www.abc.com; #绑定域名 + index index.html index.htm; + root /www/app/dist; #Vue-cli编译后的dist目录 + + location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ + { + expires 30d; + } + + location ~ .*\.(js|css)?$ + { + expires 12h; + } + + location ~ /\. + { + deny all; + } + + access_log off; #访问日志路径 + } +``` +Docker中Nginx运行命令(将上述配置文件任意命名放置于nginx_config目录中即可): +``` +$ docker run -itd -p 80:80 -p 443:443 -v `pwd`/nginx_config:/etc/nginx/conf.d nginx +``` + +## 引入插件介绍 + +> 引入插件的版本将会持续更新 + +引入的插件: +`` koa nodemon babel-cli babel-register babel-plugin-transform-runtime babel-preset-es2015 babel-preset-stage-2 gulp gulp-eslint eslint eslint-config-standard eslint-friendly-formatter eslint-plugin-html eslint-plugin-promise `` + +"koa-bodyparser": "^3.2.0", +"koa-compose": "^3.1.0", +"koa-convert": "^1.2.0", +"koa-favicon": "^2.0.0", +"koa-json": "^1.1.3", +"koa-logger": "^1.3.0", +"koa-multer": "^1.0.0", +"koa-router": "^7.0.1", +"koa-session": "^3.3.1", +"koa-static2": "^0.1.8", +"koa-onerror": "^3.0.1", + +**koa**: HTTP框架 + Synopsis: HTTP framework. + From: https://github.com/koajs/koa + +**babel-cli**: Babel编译ES6代码为ES5代码 + Synopsis: Babel is a JavaScript compiler, ES6 to ES5. + From: https://github.com/babel/babel/tree/master/packages/babel-cli + +**babel-register**: Babel开发环境实时编译ES6代码 + Synopsis: Babel hook. + From: https://github.com/babel/babel/tree/master/packages/babel-cli + +**babel-plugin-transform-runtime**: Babel配置ES6的依赖项 +**babel-preset-es2015**: 同上 +**babel-preset-stage-2**: 同上 + +**gulp**: 基于流的自动化构建工具 + Synopsis: Gulp is a toolkit for automating painful or time-consuming tasks. + From: https://github.com/gulpjs/gulp + +**gulp-eslint**: gulp的ESLint检查插件 + Synopsis: A gulp plugin for ESLint. + From: https://github.com/adametry/gulp-eslint + +**gulp-nodemon**: 修改JS代码后自动重启 + Synopsis: nodemon will watch the files in the directory in which nodemon was started, and if any files change, nodemon will automatically restart your node application. + From: https://github.com/remy/nodemon + +**eslint**: JavaScript语法检查工具 + Synopsis: A fully pluggable tool for identifying and reporting on patterns in JavaScript. + From: + +**eslint-config-standard**: 一个ESlint配置 + Synopsis: ESLint Shareable Config for JavaScript Standard Style. + From: https://github.com/feross/eslint-config-standard + +**eslint-friendly-formatter**: 使得ESlint提示在Sublime Text或iterm2中更友好,Atom也有对应的ESlint插件。 + Synopsis: A simple formatter/reporter for ESLint that's friendly with Sublime Text and iterm2 "click to open file" functionality + From: https://github.com/royriojas/eslint-friendly-formatter + +**eslint-plugin-html**: 检查HTML文件中的JS代码规范 + Synopsis: An ESLint plugin to extract and lint scripts from HTML files. + From: https://github.com/BenoitZugmeyer/eslint-plugin-html + +**eslint-plugin-promise**: 检查JavaScript promises + Synopsis: Enforce best practices for JavaScript promises. + From: https://github.com/xjamundx/eslint-plugin-promise + +**eslint-plugin-promise**: ESlint依赖项 + Synopsis: ESlint Rules for the Standard Linter. + From: https://github.com/xjamundx/eslint-plugin-standard + +支持Koa2的中间件列表:https://github.com/koajs/koa/wiki + +其它经常配合Koa2的插件: +**koa-nunjucks-2**: +一个好用的模版引擎,可用于前后端,nunjucks。https://github.com/mozilla/nunjucks + +## 目录结构说明 + +```bash +. +├── README.md +├── .babelrc # Babel 配置文件 +├── .editorconfig # 编辑器风格定义文件 +├── .eslintignore # ESlint 忽略文件列表 +├── .eslintrc.js # ESlint 配置文件 +├── .gitignore # Git 忽略文件列表 +├── gulpfile.js # Gulp配置文件 +├── package.json # 描述文件 +├── pm2.js # pm2 部署示例文件 +├── build # build 入口目录 +│   └── dev-server.js # 开发环境 Babel 实时编译入口 +├── config +│   └── main.js # 主配置文件(*谨防泄密!) +├── src # 源代码目录,编译后目标源代码位于 dist 目录 +│   ├── app.js # koa 配置 +│   ├── config # 配置目录 +│   ├── controllers # 控制器 +│   ├── index.js # 入口文件 +│   ├── models # 模型 +│   ├── routes # 路由 +│   └── services # service +└── logs # 日志目录 +``` + +## 各类主流框架调用RESTful API的示例代码(仅供参考) + +### AngularJS (Ionic同) +``` +$http({ + method: "post", + url: "http://localhost:3000/xxx", + data: {para1:"para1",para2:"para2"}, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }).success(function (data) { + }).error(function (data) { + }); + ``` + +### jQuery +``` +$.ajax({ + cache: false, + type: 'POST', + url: "http://localhost:3000/xxx", + data: { + para1: para1 + }, + async: false, + dataType: "json", + success: function (result) { + }, + error: function (err) { + console.log(err); + } +}); + +// 上传文件 +//创建FormData对象 +var data = new FormData(); +//为FormData对象添加数据 +// +$.each($('#inputfile')[0].files, function (i, file) { + data.append('upload_file', file); +}); +$.ajax({ + url: 'http://127.0.0.1:3000/api/upload_oss_img_demo', + type: 'POST', + data: data, + cache: false, + contentType: false, //不可缺 + processData: false, //不可缺 + success: function (data) { + console.log(data); + if (data.result == "ok") { + $("#zzzz").attr("src", data.img_url); + } + + } +}); +``` + +### MUI +``` +mui.ajax({ url: "http://localhost:3000/xxx", dataType: "json", + success: function(data){ + + }, + error: function(data){ + console.log("error!"); + } +}); +``` + +### JavaScript +``` + var xhr = new XMLHttpRequest() + xhr.open("POST", "http://localhost:3000/xxx", true) //POST或GET,true(异步)或 false(同步) + xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded") + xhr.withCredentials = true + xhr.onreadystatechange = function () { + if (obj.readyState == 4 && obj.status == 200 || obj.status == 304) { + var gotServices = JSON.parse(xhr.responseText) + }else{ + console.log("ajax失败了") + } + } + xhr.send({para1: para1}) +``` + +### vue-resource +https://github.com/pagekit/vue-resource +``` +// global Vue object +Vue.http.post('/someUrl', [body], { + headers: {"Content-type", "application/x-www-form-urlencoded"} +}).then(successCallback, errorCallback); +``` + +### fetch +https://github.com/github/fetch +``` +fetch('/users', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + name: 'Hubot', + login: 'hubot', + }) +}).then(function(response) { + // response.text() +}).then(function(body) { + // body +}) + +// 文件上传 +var input = document.querySelector('input[type="file"]') + +var data = new FormData() +data.append('file', input.files[0]) +data.append('user', 'hubot') + +fetch('/avatars', { + method: 'POST', + body: data +}) +``` + +### superagent +https://github.com/visionmedia/superagent +``` +request.post('/user') + .set('Content-Type', 'application/json') + .send('{"name":"tj","pet":"tobi"}') + .end(callback) +``` + +在React中可以将上述任意方法其置于componentDidMount()中,Vue.js同理。 + +## 彻底移除ESlint方法 +删除package.json的devDependencies中所有eslint开头的插件,根目录下的“.eslintignore、.eslintrc.js”文件,并且修改package.json的dev为: +``` +"dev": "gulp start" +``` +删除gulpfile.js中的lint、eslint_start两个任务,并且把default改为“gulp.task('default', ['start']”。 diff --git a/build/dev-server.js b/build/dev-server.js new file mode 100644 index 0000000..085b34e --- /dev/null +++ b/build/dev-server.js @@ -0,0 +1,2 @@ +require("babel-register") +require("../src/app") diff --git a/config/main.js b/config/main.js new file mode 100644 index 0000000..e69de29 diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..85dee3f --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,66 @@ +const gulp = require('gulp') +const eslint = require('gulp-eslint') +const nodemon = require('gulp-nodemon') +const friendlyFormatter = require('eslint-friendly-formatter') + +var jsScript = 'node' +if (process.env.npm_config_argv.indexOf('debug') > 0) { + jsScript = 'node debug' +} + +gulp.task('lint', () => { + return gulp.src(['src/*.js', '!node_modules/**']) + .pipe(eslint({configFile: '.eslintrc.js'})) + .pipe(eslint.format(friendlyFormatter)) + // .pipe(eslint.failAfterError()) + .pipe(eslint.results(results => { + // Called once for all ESLint results. + console.log(`- Total Results: ${results.length}`) + console.log(`- Total Warnings: ${results.warningCount}`) + console.log(`- Total Errors: ${results.errorCount}`) + })) +}) + +gulp.task('eslint_start', ['lint'], function () { + var stream = nodemon({ + script: 'build/dev-server.js', + execMap: { + js: jsScript + }, + tasks: ['lint'], + verbose: true, + ignore: ['build/*.js', 'dist/*.js', 'nodemon.json', '.git', 'node_modules/**/node_modules', 'gulpfile.js'], + env: { + NODE_ENV: 'development' + }, + ext: 'js json' + }) + + return stream + .on('restart', function () { + // console.log('Application has restarted!') + }) + .on('crash', function () { + console.error('Application has crashed!\n') + // stream.emit('restart', 20) // restart the server in 20 seconds + }) +}) + +gulp.task('start', function () { + return nodemon({ + script: 'build/dev-server.js', + execMap: { + js: jsScript + }, + verbose: true, + ignore: ['build/*.js', 'dist/*.js', 'nodemon.json', '.git', 'node_modules/**/node_modules', 'gulpfile.js'], + env: { + NODE_ENV: 'development' + }, + ext: 'js json' + }) +}) + +gulp.task('default', ['lint', 'eslint_start'], function () { + // console.log('ESlin检查完成') +}) diff --git a/logs/out.log b/logs/out.log new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/logs/out.log @@ -0,0 +1 @@ +ok diff --git a/package.json b/package.json new file mode 100644 index 0000000..aa693c6 --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "koa2-API-scaffold", + "version": "1.0.0", + "description": "Koa2 RESTful API 服务器的脚手架", + "author": "轶哥 ", + "scripts": { + "start": "gulp start", + "dev": "gulp", + "build": "babel src -d dist", + "production": "node dist/app.js" + }, + "dependencies": { + "koa": "^1.2.4" + }, + "devDependencies": { + "babel-cli": "^6.22.2", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-preset-es2015": "^6.22.0", + "babel-preset-stage-2": "^6.22.0", + "babel-register": "^6.22.0", + "eslint": "^3.14.0", + "eslint-config-standard": "^6.2.1", + "eslint-friendly-formatter": "^2.0.7", + "eslint-plugin-html": "^1.7.0", + "eslint-plugin-promise": "^3.4.0", + "eslint-plugin-standard": "^2.0.1", + "gulp": "^3.9.1", + "gulp-eslint": "^3.0.1", + "gulp-nodemon": "^2.2.1" + }, + "engines": { + "node": ">= 6.9.4", + "npm": ">= 3.10.10" + } +} diff --git a/pm2.js b/pm2.js new file mode 100644 index 0000000..7984191 --- /dev/null +++ b/pm2.js @@ -0,0 +1,17 @@ +module.exports = { + apps: [{ + name: 'RESRful API Server', + script: './dist/app.js', + watch: false, // 默认关闭watch 可替换为 ['src'] + ignore_watch: ['node_modules', 'build', 'logs'], + out_file: '/logs/out.log', // 日志输出 + error_file: '/logs/error.log', // 错误日志 + max_memory_restart: '2G', // 超过多大内存自动重启,仅防止内存泄露有意义,需要根据自己的业务设置 + env: { + NODE_ENV: 'production' + }, + exec_mode: 'cluster', // 开启多线程模式,用于负载均衡 + instances: 'max', // 启用多少个实例,可用于负载均衡 + autorestart: true // 程序崩溃后自动重启 + }] +} diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..4ce04d0 --- /dev/null +++ b/src/app.js @@ -0,0 +1,3 @@ + +console.log('ok') +console.log('aaa')