-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExpress
More file actions
237 lines (221 loc) · 12.9 KB
/
Copy pathExpress
File metadata and controls
237 lines (221 loc) · 12.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
Express:
- Application framework, light weight, used for web-app dev
- Can imagine as abstraction on top of http
- It sets up a routing table internally to dynamically render html pages based on input
1.)Routing:
Defines end points/ URIs for an application.
Routing method:
Methods using http verbs that defines the action to be taken in a particular end point
const express = require('express');
const app = express();//rounte
//app.listen(4000);// Server starts in port 4000. If no port mensioned, application terminates, eventhough no error printing.
//better way
app.listen(4000, ()=>{console.log('started')});-> helful to add a callback at the time of server starting
app.listen(4000, ()=>{console.log('started');}).on('error', ()=>{console.log('Failed to start')});-> this will show error message properly on address in use etc.
app.get'/getter', (req, resp)=>{//callback funcation similar for all routing methods
resp.send('message');//no need of write() then end(). This will handing everything.
});
2.)Middlewares:
Have req, res, next.
Instead of using a single callback funcation, express can rounte the req into multiple functions.
They are called middlewares.
app.get('/test', (req, res, next)=>{
console.log('L1');
next(); -> This will rediect req and resp to the next funcation, L2.
}, (req, res)=>{}console.log('L2');
res.send('Done');});-> send is required to close the stream. Else, rotates and time out.
If we call next() from L2 without passing next, error comes.
app.get('/test', (req, res, next)=>{
console.log('Level 1');
next();
res.send('Done at L1');
}, (req, res)=>{
console.log('Level 2');
res.send('Done at L2');
});-> prints L1, L2 and returns Done at L2 to front. But error comes at res.send('Done at L1'); as it tries sending again.
If we give send() and then next(); streams closes and returns that send()'s content. But next() call gets exeuted.
ie;
app.get('/test', (req, res, next)=>{
console.log('Level 1');
next();
res.send('Done at L1');
}, (req, res)=>{
console.log('Level 2');
res.send('Done at L2');
});-> Here, res.send('Done at L1'); will close the stream and gives back. But next() calls annd prints L2 also.
But in send() of L2, it gives error. Front end all fine. But backend error.
3.)Error Hnadling:
1.) When url is wrong.
Routing table is created based on order in which code for http verbs are written.
So, if we add something at the end, just before listen(), it will act as default. So;
app.use((req, res, next)=>{res.send('Error')}); will work for 404 errors, 405 etc.
2.) When an exception/ error happends inside a route,
need to handle that also.
app.use((err, req, res, next)){res.send('error')}; -> will work in that case. Give this before app.use((req, res, next)=>{res.send('Error')});
next(anything); -> will be treated as error and control goes to app.use((err, req, res, next)) if defined.
4.)Middlewares can be added as , separated funcations, array of funcations or mix of both.
app.get('/test', (req, res, next)=>{console.log('l1'); next();}, (req, res, next)=>{console.log('l1'); next();}, (req, res)=>{console.log('l1');res.send('Done');}); -> prints
=
const f1 = (req, res, next)=>{console.log('l1'); next();};
const f2 = (req, res, next)=>{console.log('l2'); next();};
const f3 = (req, res)=>{console.log('l3'); res.send('Done');};
app.get('/test', [f1, f2, f3]);
=
const f1 = (req, res, next)=>{console.log('l1'); next();};
const f2 = (req, res, next)=>{console.log('l2'); next();};
app.get('/test', [f1, f2], (req, res)=>{console.log('l3'); res.send('Done');});
5.)Stream closing:
//res.send('Got & done');
//res.end();
//res.download();
//res.json({"message":"hai"});
//res.redirect();
//res.sendFile('pakage.json');
//res.jsonp("<script>abc</script>"); -> json with padding. used before cors came
will be able to close the response stream/ req-res cycle. Else, it will be timed waiting
6.)express.Router() -> called min-app as its giving attachable routes
For more modularity
Avoids typo and redundancy
But app.get(), app.route() etc are called route handlers.route() will not accept url and then thatb route.get() etc will not accept urls.
It accepts only a funcation. ie; app.route('/test').get(()=>{//something});
express.Router() is rounter.
Router vs Route Handlers:
-------------------------
Routers gives more modularity.
But they cannot exist alone since they don't have listen() capability.
Route handlers have that capability and handlers can attach multiple routes into it.
Router() returns Router which will be capable of to create pluggable, modular, ready to mount routes.
express() and express.Router() are not the same. First one is app(handler) and second one is mini-app(router)
7.)Middleware Funcations:
Funcations with req, resp, next args.
Can modify req,res
Can end req-res cycle
Can call another middleware funcation
Can execute any code
Route level middleware - inside a router. So, only in specific route it is applicable.
Application level middleware - with app.
Error Handling Middleware:
Will have 4 parameters.
app.use((error, req, res, next)=>{
console.error(error.stack);
res.status = 500;
res.send({'message': "Internal error in app"});
});
-> What will happen if we give next() call inside error handler?
Control will go to the next possible middleware. Eg: if use() or all() is given to be executed after api call
There are many built-in middlewares. Previously it was within express. Not those are independent packages except express.static.
1.)express.static(root_file_path, options): -> options can contain remaining file path inside root folder
To access the static content such as html, css, files etc and return it as an output/ response.
app.use('/get/lib', express.static('library/img')); -> now when we call /get/lib/sample.png will display the image file if exists. Else if not found, it calls next().
If we try,
app.get('/get/lib', express.static('library/img')); -> will give 404.
Reason is;
req.path is what is using in express.static. So, in use(), req.path gives "sample.png"
but in get(), it gives, /get/lib as get() works that way. So, appending only /get/lib to library/img won't be correct pointer to imange and hence fails.
We can use
app.use('/get/lib', express.static('library/img', 'flowers')); -> if we have to fetch images inside library/img/flowers folder. Call /get/lib/lotus.jpg
2.)Other middlewares:
a.) error handler
b.) morgan -log request, res etc in given format. Eg: app.use(morgan((tokens, req, res)->{return stringToLog})); or existing formats can be used.
Eg: app.use(require('morgan')('tiny')) or app.use(morgan('tiny'))
c.) serve-favicon - favicons are small icons on title bar as a brand for website. It can be done by return image using express.static also.
But advantage of this middleware is performance improvement due to caching in memory than disk read every time.
Also, it uses ETag(special http header indicating version of a resource) and compatible content-type. Can consider as unique finger print of a website.
It helps in browser caching and update of cache.
app.use(require(favicon)('icon path'));
d.) compression - for gzip or deflate compression of response.
app.use(require('compression')()); -> can give options into it as json as well. Eg: app.use(compress({filter: true, threshold: 0
// default 1kb to start compressing}));
8.)Template Engines:
To use static template files to create html pages and fill variables on the fly.
There are many template engines. Eg: pug/ Express Application Generator prior, EJS, Mustache etc.
npm install pug --save
app.set('views', '../view'); // set folder of jade file. Its views not view.
app.set('view engine', 'pug'); // express requires view engine intenally. Just have to set this.
app.get('/test', (req, res)=>{res.render('jade-file', {title:'val1', message:'val2'});});
Sample jade file jade.pug inside view folder:
html
head
title= title // no space between title and =. Else it will not come properly.
body
h1= message
Note: Only app.set() will wor. route.set() gives error as set() is not a funcation. Route handler have set() but rounter don't have set() funcation.
9.)Body Parser middleware:
To parse request body.
Usually with req.params.abc when we pass data as /url/:abc
When we pass as query params; ie; key-value, /url with abc = 'test' from postan, use req.query.abc
In case of POST, PUT etc, body will be there which can hold, plain text, json, url encoded etc. To parse that is what the purpose of body-parser.
When we try to access req.body without parser, it will print undefined.
The reason is, req will be sending as chunks of data and hence heed to get aggregated if we want to print.
So,
parser_controller.post('/post', (req, res)=>{
console.log(req.body);// will print undefined as it is not aggreated the chunks.
res.send(req.body);// returns blank as req.body is not aggregated.
});
But,
If we try;
parser_controller.post('/post', (req, res)=>{
var data = '';
req.on('data', (chunk)=>{
console.log(req);
data += chunk;
});
req.on('end', ()=>{
res.send(data);
});
});
This will work and return proper value. Eg: Returns {"key1":"value1"} if my req body contained this same value.
This same thing can be easily achieved with lesser code as below.
const parser = require('body-parser');
parser_controller.use(parser.json());
parser_controller.post('/jpost', (req, res)=>{
console.log(req.body);
res.send(req.body);
});
Body JSON Parser:
It identifies the req content-type and if it is json or application/json,
parses the content of req.body and assign it back to req.body itself. This makes req.body a json object for further processing.
This can process/inflate gzip/deflate body.
parser.urlencoded():
To parse request bdy with content type application/x-www-form-urlencoded or */x-www-form-urlencoded.
form-urlencoded is used to send data in the form of form data. form-urlencoded
as well as multipart/formdata MIME types serves the purpose of sending HTML form data.
url encoded form send form data as a full length query string appended in body of request. When we use query param appended to url,
that is also url encoded form but not in body of request.
if we try;
POST: http://localhost:1000/bodyparser/urlblank?key1=value1
with body content-type: application/x-www-form-urlencoded, and data set in body as key2: value2;
parser_controller.use(parser.urlencoded({extended: true}));
parser_controller.post('/urlblank', (req, res)=>{
console.log(req.body);//prints { key2: 'value2' }
console.log(req.query);// prints { key1: 'value1' }
console.log(req.params);// prints {}
res.send(req.body);// returns {"key2": "value2"}
});
If we comment parser_controller.use(parser.urlencoded({extended: true}));
Then;
console.log(req.body);// undefined as no chunks aggragated
onsole.log(req.query);// prints { key1: 'value1' }
console.log(req.params);// prints {}
res.send(req.body);// returns blank
MIME Type:
Media type. Header in http request to identify the type of data contained in it.
Can related it as similar to file extensions to recognize type file it holds. Eg: .txt holds text file.
Same way, text/html indicates it as plain html file.
Form URL Encoded is one of such mime types.
Its same way as query param.
/test/key1=abc&key2=xyz abc
which will form a key value as key1 = "abc" and key2 = "xyz abc". space etc will be replaced by %20
and any other non-alpha-numberic characters replaced with %HH<hex of ascii of that special character>.
Ie;
ultimately, urlencoded parser is to parse the request body with content type url-encoded-formdata which
it converts to key value pair and reassign to req.body.
x-www-form-urlencoded vs multipart/form-data:
Both are for sending html form data to the server side.
Both transmit data as key-value pair.
urlencoded is mainly for text data whereas form-data is for binary data or file transfters.
urlencoded send data as a single query string separated by special characters with %.
Multipart form data transfer data by separating it using a character which is not part of that actual data.
Eg: use base64 to encode data and use non-base64 to split the data.
Both are used for POST or a verb with body.
form-data is much efficient for there is no need of replacing one char with 3 bytes as in case of url-encoded.