宇天 2020-09-16 16:01:46 +08:00
commit 7fdcf9562e
6 changed files with 496 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
.Spotlight-V100
.Trashes
.DS_Store
.AppleDouble
.LSOverride
._*
.idea
.vscode

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

185
Readme.md Normal file
View File

@ -0,0 +1,185 @@
![module info](https://nodei.co/npm/@gm5/response.png?downloads=true&downloadRank=true&stars=true)
# @gm5/response
> `@gm5/response` 对Http的response进一步封装, 提供常用的API.
## 安装
```bash
npm install @gm5/response
```
## 使用
```javascript
import Response from '@gm5/response'
import http from 'http'
http
.createServer((req, res) => {
let response = new Response(req, res)
// it eq. argument res
console.log(response.res)
response.set('content-type', 'text/html; charset=utf-8')
response.end('hello world')
})
.listen(3000)
```
## API
### origin
> 返回原始的 request & response 对象.
### error(msg[, code])
* msg `<String>`
* code `<Number>` Http状态码 [可选]
> 在客户端(浏览器)上输出友好的错误信息格式
```javascript
response.error('This is the error code', 500) //
response.error(null, 500) // null/empty, it will call the statusText back
response.error('Page not Found', 404) //
response.error(new Error('Auth denied'), 401) //
```
### status(code)
* code `<Number>`
> 设置Http状态码
```javascript
response.setStatus(501) //
response.setStatus(200) //
```
### set(key[, val])
* key `<String>` | `<Object>`
* code `<String>` | `<Number>`
> 设置响应头, 属性字段名不区分大小写
**相同的字段会被覆盖.**
**`content-type`如果没有设置编码时, 会自动设置为utf8**
```javascript
response.set('content-type', 'text/html; charset=utf-8') //
response.set('content-type', 'text/html') // 等价于上面的
response.set({'content-type', 'text/html', foo: 'bar'[, ...]})
```
### append(key, val)
* key `<String>`
* code `<String>` | `<Number>`
> 设置响应头, 属性字段名不区分大小写。与`set()`的区别时, 这个不会覆盖相同的字段, 而是合并输出。
```javascript
response.append('name', 'foo')
response.append('name', 'bar') //客户端能同时看到foo和bar这2个值
```
### get(key)
* key `<String>`
> 获取即将要发送到客户端的头信息。
```javascript
response.set('name', 'foo')
response.get('name') // foo
```
### redirect(url[, f])
* url `<String>`
* f `<Boolean>` 是否永久重定向, 默认否
> 重定向url.
```javascript
response.redirect('http://test.com/foo')
response.redirect('http://test.cn', true)
```
### location(url)
* url `<String>`
> 重定向url. 但这是使用前端的方式跳转的.
```javascript
response.location('http://test.com/foo')
response.location('/foo')
```
### render(data[, code])
* data `<String>` | `<Buffer>`
* code `<Number>` Http状态码, 默认200
> 以html形式渲染内容。每次请求只能调用1次。
```javascript
let html = fs.readFileSync('./index.html')
response.render(html) // send from a html file.
let txt = '<h1>hello doJS</h1>'
response.render(txt)
response.render("You're not able to here", 401)
```
### sendfile(data, filename)
* data `<String>` | `<Buffer>`
* filename `<String>`
> 直接以附件形式响应, 作为文件下载功能.
```javascript
let pic = fs.readFileSync('./boy.jpg')
response.sendfile(pic, 'a-little-boy.jpg') //
```
### send(code[, msg][, data][, callback])
* code `<Number>` http状态码
* msg `<String>` 错误信息文本
* data `<Object>` 响应主体内容, 可以是任意格式
* callback `<String>` 以jsonp形式返回对应的callback名
> 向客户端输出一个json(p), 支持resful api。
```javascript
response.send(200, 'ok', { foo: 'bar' })
// client will get the content like
// '{"code": 200, "msg": "ok", "data": {"foo": "bar"}}'
response.send(200, 'success', { name: 'foo', age: 16 }, 'blabla')
// client will get the content like
// 'blabla({"code": 200, "msg": "success", "data": {"name": "foo", "age": 16}})'
```
### end([data])
* data `<String>` | `<Buffer>` optional
> 向客户端输出内容。

202
index.js Normal file
View File

@ -0,0 +1,202 @@
/**
* {description of this file}
* @author yutent<yutent.io@gmail.com>
* @date 2020/09/16 14:52:58
*/
import STATUS_TEXT from './lib/http-code-msg.json'
const CHARSET_REGEXP = /;\s*charset\s*=/
export default class Response {
constructor(req, res) {
this.origin = { req, res }
this.rendered = false
}
/**
* [error http 错误显示]
* @param {Number} code [http错误码]
* @param {String} msg [错误提示信息]
*/
error(msg, code = 500) {
if (this.rendered) {
return
}
msg = msg || STATUS_TEXT[code]
this.status(code)
this.set('Content-Type', 'text/html; charset=utf-8')
this.end(
`<fieldset><legend>Http Status: ${code}</legend><pre>${msg}</pre></fieldset>`
)
}
status(code = 404) {
this.statusCode = code
}
/**
* [append 往header插入信息]
* @param {String} key [description]
* @param {String} val [description]
*/
append(key, val) {
if (this.rendered) {
return
}
let prev = this.get(key)
let value = val
if (prev) {
if (Array.isArray(prev)) {
value = prev.concat(val)
} else if (Array.isArray(val)) {
value = [prev].concat(val)
} else {
value = [prev, val]
}
}
return this.set(key, value)
}
/**
* [redirect 页面跳转]
* @param {String} url [要跳转的URL]
* @param {Boolean} f [是否永久重定向]
*/
redirect(url, f = false) {
if (this.rendered) {
return
}
if (!/^(http[s]?|ftp):\/\//.test(url)) {
url = '//' + url
}
this.set('Location', url)
this.status(f ? 301 : 302)
this.end('')
}
/**
* [location 页面跳转(前端的方式)]
*/
location(url) {
var html = `<html><head><meta http-equiv="refresh" content="0;url=${url}"></head></html>`
if (this.rendered) {
return
}
this.render(html)
}
// 以html格式向前端输出内容
render(data, code) {
if (this.rendered) {
return
}
data += ''
data = data || STATUS_TEXT[code]
this.set('Content-Type', 'text/html')
this.set('Content-Length', Buffer.byteLength(data))
if (code) {
this.status(code)
}
this.end(data)
}
// 文件下载
sendfile(data, filename) {
if (this.rendered) {
return
}
this.set('Content-Type', 'application/force-download')
this.set('Accept-Ranges', 'bytes')
this.set('Content-Length', Buffer.byteLength(data))
this.set('Content-Disposition', `attachment;filename="${filename}"`)
this.end(data)
}
/**
* [send json格式输出]
* @param {Num} code [返回码]
* @param {Str} msg [提示信息]
* @param {Str/Obj} data [额外数据]
* @param {Str} callback [回调函数名]
*/
send(code = 200, msg = 'success', data = null, callback = null) {
var output
if (this.rendered) {
return
}
if (typeof code !== 'number') {
msg = code + ''
code = 400
} else if (typeof msg === 'object') {
data = msg
code = code || 200
msg = STATUS_TEXT[code] || 'success'
}
output = { code, msg, data }
output = JSON.stringify(output)
if (callback) {
callback = callback.replace(/[^\w\-\.]/g, '')
output = callback + '(' + output + ')'
}
this.set('Content-Type', 'application/json')
this.set('Content-Length', Buffer.byteLength(output))
// 只设置200以上的值
if (code && code > 200) {
this.status(code)
}
this.end(output)
}
end(buf) {
var code = 200
if (this.rendered) {
return this
}
if (this.statusCode) {
code = this.statusCode
delete this.statusCode
}
this.rendered = true
this.origin.res.writeHead(code, STATUS_TEXT[code])
this.origin.res.end(buf || '')
}
/**
* [get 读取已写入的头信息]
*/
get(key) {
return this.origin.res.getHeader(key)
}
/**
* [set 设置头信息]
*/
set(key, val) {
if (this.rendered) {
return this
}
if (arguments.length === 2) {
let value = Array.isArray(val) ? val.map(String) : String(val)
if (key.toLowerCase() === 'content-type' && !CHARSET_REGEXP.test(value)) {
value += '; charset=utf-8'
}
this.origin.res.setHeader(key, value)
} else {
for (let i in key) {
this.set(i, key[i])
}
}
return this
}
}

68
lib/http-code-msg.json Normal file
View File

@ -0,0 +1,68 @@
{
"100": "Continue",
"101": "Switching Protocols",
"102": "Processing",
"200": "OK",
"201": "Created",
"202": "Accepted",
"203": "Non-Authoritative Information",
"204": "No Content",
"205": "Reset Content",
"206": "Partial Conten",
"207": "Multi-Status",
"208": "Already Reported",
"226": "IM Used",
"300": "Multiple Choices",
"301": "Moved Permanently",
"302": "Found",
"303": "See Other",
"304": "Not Modified",
"305": "Use Proxy",
"306": "Switch Proxy",
"307": "Temporary Redirect",
"308": "Permanent Redirect",
"400": "Bad Request",
"401": "Unauthorized",
"402": "Payment Required",
"403": "Forbidden",
"404": "Not Found",
"405": "Method Not Allowed",
"406": "Not Acceptable",
"407": "Proxy Authentication Required",
"408": "Request Timeout",
"409": "Conflict",
"410": "Gone",
"411": "Length Required",
"412": "Precondition Failed",
"413": "Request Entity Too Large",
"414": "Request-URI Too Long",
"415": "Unsupported Media Type",
"416": "Requested Range Not Satisfiable",
"417": "Expectation Failed",
"418": "I'm a teapot",
"420": "Enhance Your Caim",
"421": "Misdirected Request",
"422": "Unprocessable Entity",
"423": "Locked",
"424": "Failed Dependency",
"425": "Unordered Collection",
"426": "Upgrade Required",
"428": "Precondition Required",
"429": "Too Many Requests",
"431": "Request Header Fields Too Large",
"444": "No Response",
"450": "Blocked by Windows Parental Controls",
"451": "Unavailable For Legal Reasons",
"494": "Request Header Too Large",
"500": "Internal Server Error",
"501": "Not Implemented",
"502": "Bad Gateway",
"503": "Service Unavailable",
"504": "Gateway Timeout",
"505": "HTTP Version Not Supported",
"506": "Variant Also Negotiates",
"507": "Insufficient Storage",
"508": "Loop Detected",
"510": "Not Extended",
"511": "Network Authentication Required"
}

11
package.json Normal file
View File

@ -0,0 +1,11 @@
{
"name": "@gm5/response",
"version": "1.0.0",
"type": "module",
"description": "对Http的response进一步封装, 提供常用的API",
"main": "index.js",
"author": "yutent",
"keywords": ["fivejs", "response", "http"],
"repository": "https://github.com/bytedo/gm5.response.git",
"license": "MIT"
}