Skip to content

Commit 49110f2

Browse files
committed
feat: accepts* methods added to Request object`
1 parent 003d60e commit 49110f2

File tree

5 files changed

+248
-11
lines changed

5 files changed

+248
-11
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,13 @@ Methods:
9797

9898
| Method | Notes |
9999
|-------------|-------|
100+
| [accepts()](https://expressjs.com/en/4x/api.html#req.accepts) | - |
101+
| [acceptsEncodings()](https://expressjs.com/en/4x/api.html#req.acceptsEncodings) | - |
102+
| [acceptsCharsets()](https://expressjs.com/en/4x/api.html#req.acceptsCharsets) | - |
103+
| [acceptsLanguages()](https://expressjs.com/en/4x/api.html#req.acceptsLanguages) | - |
100104
| [get()](https://expressjs.com/en/4x/api.html#req.get) | - |
101-
| [is()](https://expressjs.com/en/4x/api.html#req.is) | Doesn't support `*` wildcard checks(like `text/*`) |
105+
| [header()](https://expressjs.com/en/4x/api.html#req.header) | - |
106+
| [is()](https://expressjs.com/en/4x/api.html#req.is) | - |
102107

103108
### Response Object
104109

package-lock.json

Lines changed: 16 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,9 @@
2323
"body-parser": "^1.19.0",
2424
"jest": "^24.9.0",
2525
"semantic-release": "^15.13.21"
26+
},
27+
"dependencies": {
28+
"accepts": "^1.3.7",
29+
"type-is": "^1.6.18"
2630
}
2731
}

src/request.js

Lines changed: 168 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
const ReadableStream = require('stream').Readable;
2+
const accepts = require('accepts');
3+
const typeis = require('type-is');
24

35
/**
46
*
57
*/
68
class Request extends ReadableStream {
79
constructor(event) {
810
super();
11+
912
this.headers = Object.keys(event.headers).reduce((headers, key) => {
1013
headers[key.toLowerCase()] = event.headers[key];
1114
return headers;
1215
}, {});
16+
1317
this.hostname = this.headers.host
1418
this.method = event.httpMethod;
1519
this.query = event.queryStringParameters;
@@ -24,17 +28,178 @@ class Request extends ReadableStream {
2428
this.xhr = (this.get('X-Requested-With') || '').toLowerCase() === 'xmlhttprequest';
2529

2630
this.event = event;
31+
this.accept = accepts(this);
2732

2833
this.push(event.body);
2934
this.push(null);
3035
}
3136

37+
/**
38+
* Check if the given `type(s)` is acceptable, returning
39+
* the best match when true, otherwise `undefined`, in which
40+
* case you should respond with 406 "Not Acceptable".
41+
*
42+
* The `type` value may be a single MIME type string
43+
* such as "application/json", an extension name
44+
* such as "json", a comma-delimited list such as "json, html, text/plain",
45+
* an argument list such as `"json", "html", "text/plain"`,
46+
* or an array `["json", "html", "text/plain"]`. When a list
47+
* or array is given, the _best_ match, if any is returned.
48+
*
49+
* Examples:
50+
*
51+
* // Accept: text/html
52+
* req.accepts('html');
53+
* // => "html"
54+
*
55+
* // Accept: text/*, application/json
56+
* req.accepts('html');
57+
* // => "html"
58+
* req.accepts('text/html');
59+
* // => "text/html"
60+
* req.accepts('json, text');
61+
* // => "json"
62+
* req.accepts('application/json');
63+
* // => "application/json"
64+
*
65+
* // Accept: text/*, application/json
66+
* req.accepts('image/png');
67+
* req.accepts('png');
68+
* // => undefined
69+
*
70+
* // Accept: text/*;q=.5, application/json
71+
* req.accepts(['html', 'json']);
72+
* req.accepts('html', 'json');
73+
* req.accepts('html, json');
74+
* // => "json"
75+
*
76+
* @param {String|Array} type(s)
77+
* @return {String|Array|Boolean}
78+
* @public
79+
*/
80+
accepts() {
81+
return this.accept.types.apply(this.accept, arguments);
82+
}
83+
84+
/**
85+
* Check if the given `encoding`s are accepted.
86+
*
87+
* @param {String} ...encoding
88+
* @return {String|Array}
89+
* @public
90+
*/
91+
acceptsEncodings() {
92+
return this.accept.encodings.apply(this.accept, arguments);
93+
}
94+
95+
/**
96+
* Check if the given `charset`s are acceptable,
97+
* otherwise you should respond with 406 "Not Acceptable".
98+
*
99+
* @param {String} ...charset
100+
* @return {String|Array}
101+
* @public
102+
*/
103+
acceptsCharsets() {
104+
return this.accept.charsets.apply(this.accept, arguments);
105+
}
106+
107+
/**
108+
* Check if the given `lang`s are acceptable,
109+
* otherwise you should respond with 406 "Not Acceptable".
110+
*
111+
* @param {String} ...lang
112+
* @return {String|Array}
113+
* @public
114+
*/
115+
acceptsLanguages() {
116+
return this.accept.languages.apply(this.accept, arguments);
117+
}
118+
119+
/**
120+
* Return request header.
121+
*
122+
* The `Referrer` header field is special-cased,
123+
* both `Referrer` and `Referer` are interchangeable.
124+
*
125+
* Examples:
126+
*
127+
* req.get('Content-Type');
128+
* // => "text/plain"
129+
*
130+
* req.get('content-type');
131+
* // => "text/plain"
132+
*
133+
* req.get('Something');
134+
* // => undefined
135+
*
136+
* Aliased as `req.header()`.
137+
*
138+
* @param {String} name
139+
* @return {String}
140+
* @public
141+
*/
32142
get(key) {
33-
return this.headers[key.toLowerCase()];
143+
return this.header(key);
144+
}
145+
146+
header(name) {
147+
if (!name) {
148+
throw new TypeError('name argument is required to req.get');
149+
}
150+
151+
if (typeof name !== 'string') {
152+
throw new TypeError('name must be a string to req.get');
153+
}
154+
155+
const lc = name.toLowerCase();
156+
157+
switch (lc) {
158+
case 'referer':
159+
case 'referrer':
160+
return this.headers.referrer
161+
|| this.headers.referer;
162+
default:
163+
return this.headers[lc];
164+
}
34165
}
35166

36-
is(mimeType) {
37-
return this.get('Content-Type') && this.get('Content-Type').indexOf(mimeType) > -1;
167+
/**
168+
* Check if the incoming request contains the "Content-Type"
169+
* header field, and it contains the give mime `type`.
170+
*
171+
* Examples:
172+
*
173+
* // With Content-Type: text/html; charset=utf-8
174+
* req.is('html');
175+
* req.is('text/html');
176+
* req.is('text/*');
177+
* // => true
178+
*
179+
* // When Content-Type is application/json
180+
* req.is('json');
181+
* req.is('application/json');
182+
* req.is('application/*');
183+
* // => true
184+
*
185+
* req.is('html');
186+
* // => false
187+
*
188+
* @param {String|Array} types...
189+
* @return {String|false|null}
190+
* @public
191+
*/
192+
is(types) {
193+
var arr = types;
194+
195+
// support flattened arguments
196+
if (!Array.isArray(types)) {
197+
arr = new Array(arguments.length);
198+
for (var i = 0; i < arr.length; i++) {
199+
arr[i] = arguments[i];
200+
}
201+
}
202+
return typeis(this, arr);
38203
}
39204
}
40205

src/request.spec.js

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,63 @@ describe('Request object', () => {
2525

2626
expect(request.get('Content-Type')).toBe('application/json');
2727
expect(request.get('content-type')).toBe('application/json');
28+
});
29+
30+
it('should handle weird header asks', () => {
31+
const request = new Request(event);
32+
33+
expect(() => request.get()).toThrow(TypeError('name argument is required to req.get'));
34+
expect(() => request.get({})).toThrow(TypeError('name must be a string to req.get'));
35+
});
36+
37+
it('should read referer/referrer header', () => {
38+
const referer = 'muratcorlu.com';
39+
event.headers['Referer'] = referer;
40+
41+
const request = new Request(event);
42+
expect(request.get('referer')).toBe(referer);
43+
expect(request.get('referrer')).toBe(referer);
2844
})
2945

3046
it('check type', () => {
3147
const request = new Request(event);
32-
expect(request.is('json')).toBe(true);
48+
expect(request.is('json')).toBe('json');
49+
expect(request.is(['html', 'json'])).toBe('json');
50+
expect(request.is('html', 'xml')).toBe(false);
3351
})
52+
53+
it('should check accept header', () => {
54+
event.headers['Accept'] = 'application/json';
55+
56+
const request = new Request(event);
57+
expect(request.accepts('xml')).toBe(false);
58+
expect(request.accepts('text/xml')).toBe(false);
59+
expect(request.accepts('json')).toBe('json');
60+
expect(request.accepts('application/json')).toBe('application/json');
61+
expect(request.accepts(['html', 'json'])).toBe('json');
62+
})
63+
64+
it('should check acceptEncodings', () => {
65+
event.headers['accept-encoding'] = 'gzip, compress;q=0.2';
66+
67+
const request = new Request(event);
68+
expect(request.acceptsEncodings('gzip', 'compress')).toBe('gzip');
69+
70+
});
71+
72+
it('should check acceptsCharsets', () => {
73+
event.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5';
74+
75+
const request = new Request(event);
76+
expect(request.acceptsCharsets('utf-7', 'utf-8')).toBe('utf-8');
77+
78+
});
79+
80+
it('should check acceptsLanguages', () => {
81+
event.headers['accept-charset'] = 'en;q=0.8, es, tr';
82+
83+
const request = new Request(event);
84+
expect(request.acceptsLanguages('tr', 'en')).toBe('tr');
85+
86+
});
3487
});

0 commit comments

Comments
 (0)