1.0.0
commit
7fdcf9562e
|
@ -0,0 +1,9 @@
|
|||
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
._*
|
||||
.idea
|
||||
.vscode
|
|
@ -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.
|
|
@ -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
|
||||
|
||||
> 向客户端输出内容。
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
Loading…
Reference in New Issue