This is the 28th day of my participation in the August Text Challenge.More challenges in August
1. Service scenario of file uploading
In addition to the text character type data we normally submit, many times we submit data in other formats such as images, audio, video, and so on.
- Upload user profile picture
- Upload web disk files
- …
2. Client data submission
Regardless of the client environment (e.g., browser, Postman, Thunderbolt, or whatever), the most basic and important thing is the protocol used by the client and server.
2-1. Send the request using a browser
As mentioned above, to send a request is to use the above canonical format, link to the server and submit some data (resources) and get the data (resources) returned by the server. These contents include:
agreement
How and in what format data resources interact.
URL
Location of the data resource
Request method
What to do with this data resource: get? To add? Modified? Replace? To delete?
Header information
Additional description (metadata) of the resource request.
The body of the
The actual data that needs to be submitted when adding, modifying, or replacing data.
2-2. Submit the request using a browser
When we use the address bar provided by the browser to send a request, we can only set the URL in it, other parts of the browser default:
- Request method:
GET
。 - Header information: Automatically set by the browser (varies by browser).
- Text: According to
HTTP
Protocol Specification DefinitionGET
The body cannot be submitted.
Only simple GET requests can be sent in the address bar of a browser.
2-3. Submit the request using a form in HTML
Forms in HTML provide more request Settings than the browser itself.
<form action="/postData" method="post" enctype="application/x-www-form-urlencoded">
<p>Name:<input type="text" name="username" />
</p>
<p>
<button>submit</button>
</p>
</form>
Copy the code
action
Request the address
method
Request method, form only support: GET, POST.
enctype
The format of the data in the request body corresponds to: content-type in the request header. There are three values:
- Application/X-www-form-urlencoded: URL format coded data,
enctype
The default value of. - Multipart /form-data: Array (raw data) encoded in form-data format – used when uploading files.
- Text /plain: indicates plain text data.
Note:
Content-type Specifies the Type of data, but does not necessarily indicate that the data is in that format. Such as: Your data is organized in JSON format, but you can also set the content-type to text/ HTML. Generally, the receiver uses the content-type to identify the Type and call the corresponding method to parse it. So if the Content-Type doesn’t match the actual Content format, it can cause the receiver to parse incorrectly.
3. Webserver receives and processes the submitted data
When the server receives the request, it actually obtains all the data related to the request. Net and HTTP modules in Node.js encapsulate the parsing of these data and provide the corresponding API to obtain them:
//C3-0. server.on('request'.(req, res) = > {
console.log('Request:', req.method);
console.log('HTTP Protocol Version ', req.httpVersion);
console.log('Request URL:', req.url);
console.log('Request Header:', req.headers); })...Copy the code
Based on the structure defined by the HTTP standard specification, we usually conduct some data interaction between client and server in the following manner.
3-1, URL
The URL requested by the client is itself a kind of data.
//C3-1-0. server.on('request'.async (req, res) => {
// The requested URL is a set of data. The server can do different things with different URL data and return different results
if (req.url == '/') {
res.end('index');
}
if (req.url == '/register') {
res.end('register');
}
// Dynamic routing
let dyRouter = /\/item\/(?
\d+)/gi
.exec(req.url);
if (dyRouter) {
let { id } = dyRouter.groups;
res.end(`id: ${id}`); }}); .Copy the code
// Use koa & koa-router
// C3-1-1-1. router.get('/item/:id(\d+)'.async (ctx, next) => {
let {id} = ctx.params;
res.end(`id: ${id}`); })...Copy the code
In the 3-1-2 s, the QueryString
QueryString is in the URL, right? For the rest, additional arguments are provided to the server using an ampersand separated list of key/value pairs, such as:? Key1 = value1 & key2 = value2. The server can use these parameters to perform additional operations.
// C3-1-2-0.// http://localhost:8888/items? page=1&limit=5
server.on('request'.(req, res) = > {
const u = new URL(`${req.headers.host}${req.url}`);
const query = u.searchParams;
console.log(`page: ${query.get('page')} , limit: ${query.get('limit')}`); }); .Copy the code
// C3-1-2-1.// http://localhost:8888/items? page=1&limit=5
router.get('/item'.async (ctx, next) => {
let {page, limit} = ctx.query;
console.log(`page: ${page} , limit: ${limit}`); })...Copy the code
QueryString is part of the URL, so it has nothing to do with the request method, i.e., whatever the request is (GET/POST/PUT/….). You can have a queryString.
3-2, the text
The main data for the body request and response. In the HTTP protocol standard, body data is not always portable:
Reference: developer.mozilla.org/zh-CN/docs/…
Body data often involves complex data, so the sender needs to organize the data in a format that tells the receiver the MIME Type (organization structure) of the data through the content-Type header.
application/x-www-form-urlencoded
A data format encoded in URLEncode, used in QueryString.
Reference: www.eso.org/~ndelmott/u…
application/json
As the name implies, it is in JSON format.
text/plain
If the data does not require any processing (even if it is JSON-structured), the recipient can be told via Text /plain.
multipart/form-data
Some of the above types are based on character data, while others contain not only characters but other types, such as binary data. At this point, we can format the data with form-data and declare the content-Type as multipart/form-data; A boundary = random.
Reference: developer.mozilla.org/zh-CN/docs/…
3-2-1, node.js based native text parsing processing
// C3-2-1-0
const Koa = require('koa');
const KoaRouter = require('koa-router');
const queryString = require('querystring');
const server = new Koa();
const router = new KoaRouter();
// Body parsing middleware
const koaBody = () = > {
return (ctx, next) = > {
return new Promise((resolve, reject) = > {
let data = ' ';
// ctx.req => the IncomingMessage object of the HTTP module in Node.js
// Data event: continuously emitted during receiving data
// chunk: received binary buffer data streams
ctx.req.on('data'.async (chunk) => {
// Concatenate the received data
data += chunk.toString();
});
// end: data receiving is triggered
ctx.req.on('end'.async() = > {Ctx. is a method provided by the wrapper under the Reuqest object in Koa,
// Verifies that the value of 'content-type' in the current request is one of the values specified in the parameter
// Content-Type: application/json
// ctx.is(['application/json', 'application/x-www-form-urlencoded']) returns Application /json
if (ctx.is('application/json')) {
// Perform JSON parsing on the data
ctx.request.body = JSON.parse(data);
} else {
// QueryString+urldecoded parse the data
ctx.request.body = queryString.parse(data);
} else {
// If the above processing is not met, the original string is used by default
ctx.request.body = data;
}
// Use the above parsing to save the parsed results to ctx.request.body for subsequent middleware calls
// The same is true of the basic principles of the KOA-body middleware
resolve();
});
}).then((res) = > {
return next();
})
}
}
router.post('/user', koaBody(), async (ctx, next) => {
console.log('body', ctx.request.body);
ctx.body = 'user';
});
server.use(router.routes());
server.listen(8888);
Copy the code
3-2-2, use of KOA-body middleware in Koa
In KOA, the koA-body middleware is used to handle the parsing of body content
Reference: www.npmjs.com/package/koa…
// C3-2-2-0
const Koa = require('koa');
const KoaRouter = require('koa-router');
const koaBody = require('koa-body');
const server = new Koa();
const router = new KoaRouter();
router.post('/user', koaBody({
/ / koa - body configuration
}), async (ctx, next) => {
ctx.body = 'user';
});
server.use(router.routes());
server.listen(8888);
Copy the code
Koa-body configuration description
-
patchNode
{Boolean} Patch request body to Node’sctx.req
, defaultfalse
- Whether to store the parsed content to
ctx.req
Property, the default is:false
.
- Whether to store the parsed content to
-
patchKoa
{Boolean} Patch request body to Koa’sctx.request
, defaulttrue
- Whether to store the parsed content to
ctx.request
Property, the default is:true
.
- Whether to store the parsed content to
-
jsonLimit
{String|Integer} The byte (if integer) limit of the JSON body, default1mb
- Set up the
JSON
Format Data size. Supports both numeric and string values, such as'1kb'
或100
If the value is a number, the unit isbyte
, the default is:1mb
。
- Set up the
-
formLimit
{String|Integer} The byte (if integer) limit of the form body, default56kb
- with
jsonLimit
To set upapplication/x-www-form-urlencoded
Value size, default56kb
。
- with
-
textLimit
{String|Integer} The byte (if integer) limit of the text body, default56kb
- with
jsonLimit
To set uptext/plain
Value size, default56kb
。
- with
-
encoding
{String} Sets encoding for incoming form fields, defaultutf-8
- Set request data encoding, default
utf-8
。
- Set request data encoding, default
-
multipart
{Boolean} Parse multipart bodies, defaultfalse
-
Whether to enable parsing multipart/form-data. The default value is false.
-
Note: This defaults to false, so if you want to use koa-body to process multipart/form-data data, remember to turn this on.
-
-
urlencoded
{Boolean} Parse urlencoded bodies, defaulttrue
- Enabled or not
application/x-www-form-urlencoded
Data parsing, default istrue
。
- Enabled or not
-
text
{Boolean} Parse text bodies, such as XML, defaulttrue
- Enabled or not
text/plain
Data parsing, default istrue
。
- Enabled or not
-
json
{Boolean} Parse JSON bodies, defaulttrue
- Enabled or not
applicatin/json
Data parsing, default istrue
。
- Enabled or not
-
jsonStrict
{Boolean} Toggles co-body strict mode; if set to true – only parses arrays or objects, defaulttrue
- Whether to enable
co-body
Strict parsing mode if set totrue
Is only processedarrays
和objects
By default,true
- Whether to enable
-
includeUnparsed
{Boolean} Toggles co-body returnRawBody option; if set to true, for form encoded and JSON requests the raw, unparsed requesty body will be attached toctx.request.body
using aSymbol
, defaultfalse
- Whether to append the original content for parsing to
ctx.request.body
On the properties (encoded
和json
, the default isfalse
。
- Whether to append the original content for parsing to
-
formidable
{Object} Options to pass to the formidable multipart parser- Set file upload options, object format.
koa-body
Other third-party libraries are usednode-formidable
Implement file upload, so this setting is set for this third-party library.- You need to set both
multipart
Options fortrue
。
-
onError
{Function} Custom error handle, if throw an error, you can customize the response – onError(error, context), default will throw- Custom error handlers.
-
parsedMethods
{String[]} Declares the HTTP methods where bodies will be parsed, default['POST', 'PUT', 'PATCH']
.- Set up the
koa-body
What will it behttp
Request method, the default is['POST', 'PUT', 'PATCH']
。
- Set up the
Formidable configuration
By default, results for file types are stored in the ctx.request.files property, and results for non-file types (characters) are stored in ctx.request.body.
-
maxFields
{Integer} Limits the number of fields that the querystring parser will decode, default1000
- Set up the
queryString
Maximum number of fields, default1000
。
- Set up the
-
maxFieldsSize
{Integer} Limits the amount of memory all fields together (except files) can allocate in bytes. If this value is exceeded, an ‘error’ event is emitted, default2mb (2 * 1024 * 1024)
- Set the maximum content size for non-file data. Default
2mb
。
- Set the maximum content size for non-file data. Default
-
uploadDir
{String} Sets the directory for placing file uploads in, defaultos.tmpDir()
-
Upload a directory for saving files. The default for the OS. TmpDir ()
-
OS is the built-in system module of Node.js. The tmpDir() method is used to obtain the system temporary directory under the current system.
-
-
keepExtensions
{Boolean} Files written touploadDir
will include the extensions of the original files, defaultfalse
- Indicates whether to retain the suffix (extension) of the uploaded file. The default value is
false
。
- Indicates whether to retain the suffix (extension) of the uploaded file. The default value is
-
hash
{String} If you want checksums calculated for incoming files, set this to either'sha1'
or'md5'
, defaultfalse
- Whether to verify the fingerprint of uploaded files. The default value is
false
。
- Whether to verify the fingerprint of uploaded files. The default value is
-
multiples
{Boolean} Multiple file uploads or no, defaulttrue
- Whether multiple files can be uploaded. The default value is
true
。
- Whether multiple files can be uploaded. The default value is
-
onFileBegin
{Function} Special callback on file begin. The function is executed directly by formidable. It can be used to rename files before saving them to disk.- File upload event callback function, which can be used with the return value to change the name of the uploaded file (default uploaded file is used
hash
To name it.
- File upload event callback function, which can be used with the return value to change the name of the uploaded file (default uploaded file is used
Reference: github.com/node-formid…
4. What is stateless in HTTP? What’s wrong with it?
4-1. Stateless
The so-called state can be understood as the memory of transaction processing. Stateless means no memory. If there is a need to have a state, the request needs to be maintained in the memory of the request, which will bring burden. Meanwhile, the real-time requirements of the Web are not as high as real-time chat, voice and video calls or games, which will lead to the waste of server resources.
One problem with this, of course, is the inability to process associated transactions, such as users who are identified in some requests but not in the next.
4-2, have a conversation
Session refers to a communication process between the client and the server. Although stateless feature does not proactively maintain the state of the session in real time, we can actively establish some associations in each session. The cookie mechanism in HTTP can be used to store and transmit this session state.
5. Functions and characteristics of cookies
5-1. Create a cookie
If we set up a ‘stateful’ session first, we need:
1. When the server receives a request to establish the state (such as a login request), the server can return the response with a set-cookie header option that can represent the current client and the server (such as generating a unique sessionID).
2. The client receives set-cookie data and saves it (for example, the browser will automatically save it in a specified location).
3. Each request of the client will find the cookie related to the server of the current request from the specified location of cookie storage, and send the cookie request to the server.
In this way, a so-called ‘stateful’ session can be established between the client and the server.
const http = require('http'); const queryString = require('querystring'); const server = http.createServer(); server.on('request', (req, res) => { res.setHeader('Content-Type', 'text/html; charset=utf-8'); if (req.url == '/user') { let cookies = null; try { cookies = queryString.parse(req.headers.cookie, '; '); } catch (e) { res.statusCode = 401; Res.end (' no permissions '); } if (! cookies.user) { res.statusCode = 401; Res.end (' no permissions '); } else { res.end('Kobe Bryant and James'); } } if (if (req.method.toLowerCase() == 'post' && req.url == '/login') {) { let user = { id: 1, username: 'Kobe Bryant' }; // Set cookie res.setheader (' set-cookie ', 'user=${json.stringify (user)}'); . / / Set up multiple cookies / / res setHeader (' Set - cookies' [' a = 1 ', 'b = 2]). Res.end (' Authorization succeeded '); }}); server.listen(8888);Copy the code
Reference: developer.mozilla.org/zh-CN/docs/…
6. Use cookies in KOA to implement user session authentication
// C6-0-0
const Koa = require('koa');
const KoaRouter = require('koa-router');
const server = new Koa();
const router = new KoaRouter();
router.get('/user'.async (ctx, next) => {
let user = null;
try {
user = ctx.cookies.get('user');
} catch (e) {
ctx.throw(401.'No permissions');
}
if(! user) { ctx.throw(401.'No permissions');
} else {
ctx.body = 'Kobe Bryant and James'; }}); router.post('/login'.async (ctx, next) => {
let user = {
id: 1.username: 'Kobe Bryant'
};
/ / set the cookie
ctx.cookies.set('user'.JSON.stringify(user));
// Set multiple cookies
// ctx.cookies.set('a', 1);
// ctx.cookies.set('b', 2);
ctx.body = 'Authorization successful';
})
server.use(router.routes());
server.listen(8888);
Copy the code
6-1. Cookie validity verification under Koa
To avoid Cookie tampering on the client side or during transmission, we can introduce some relevant validation schemes:
// C6-1-0
const Koa = require('koa');
const KoaRouter = require('koa-router');
const server = new Koa();
const router = new KoaRouter();
// The secret key used to generate the signature string
server.keys = ['KobeBryant'];
router.get('/user'.async (ctx, next) => {
let user = null;
try {
user = ctx.cookies.get('user', {
// Whether to verify the current cookie signature
signed: true
});
} catch (e) {
ctx.throw(401.'No permissions');
}
if(! user) { ctx.throw(401.'No permissions');
} else {
ctx.body = 'Kobe Bryant and James'; }}); router.post('/login'.async (ctx, next) => {
let user = {
id: 1.username: 'Kobe Bryant'
};
/ / set the cookie
ctx.cookies.set('user'.JSON.stringify(user), {
// Whether to generate signatures at the same time
signed: true
});
// Set multiple cookies
// ctx.cookies.set('a', 1);
// ctx.cookies.set('b', 2);
ctx.body = 'Authorization successful';
})
server.use(router.routes());
server.listen(8888);
Copy the code
When sending cookies, the server generates a signed HASH string (as in this case, based on the key value of the current Cookie + secret key + SHA256) according to the specified algorithm and sends it to the client at the same time.
The client request will return the Cookie and the corresponding HASH to the server, and the back end will HASH the Cookie and the secret key saved by the client, and compare the calculated HASH with the HASH sent through the request. In this way, both the Cookie and the sent HASH will be different if they have been modified. To verify that the Cookie has been tampered with.
Protect the secret key!
6-2. Life cycle
Cookie can also be set to its valid time, corresponding to two values respectively:
-
Session: The default value. If the expiration time of a Cookie is set to session, then the browser closes and the session is automatically deleted.
-
Persistence, an expiration date specified by Expires or max-age
Setting Mode:
Expires=
Optional, set the current cookie expiration time, syntax:
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Copy the code
Max-Age=
Optional, the number of seconds that need to pass before the cookie expires. A number of seconds of 0 or -1 will expire the cookie directly. Some older browsers (IE6, IE7, and IE8) do not support this property. For other browsers, if both Expires and max-age are present, then max-age takes precedence.
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit>
Copy the code
6-3. Scope
The Domain and Path identifiers define the * scope of cookies: * which urls are allowed to send cookies. Grammar:
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Copy the code
6-4. Restrict access
There are two ways to ensure that cookies are sent securely and not accessed by unexpected actors or scripts: the Secure property and the HttpOnly property.
- Marked as
Secure
的Cookie
Should only pass byHTTPS
Protocol encrypted requests are sent to the server. - JavaScript
Document.cookie
API cannot access withHttpOnly
Properties of theCookie
, limited toHTTP
Transmission use.
Grammar:
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
Copy the code
6-5. Use cases
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly
Copy the code
Reference: developer.mozilla.org/zh-CN/docs/…