This article is participating in node.js advanced technology essay, click to see details.
Serve-static is middleware that provides static file hosting services in Node, behind which is middleware encapsulation of the SEND library. The library maps to the resource based on the requested req.URL and does not respond directly to 404 when the resource does not exist, but instead calls next() to move on to the next middleware. NodeJs static file hosting service implementation principle of this article.
Brothers, I don’t know if you can stand it or not!! Ding in!! Ding in!! Top in!!
Without further ado, let’s start with the basics.
The basic use
- Use a static file hosting service in an HTTP service
const http = require('http');
const finalhandler = require('finalhandler');
const serveStatic = require('serve-static');
const root = __dirname + '/static';
const fileServer = serveStatic(root, {
index: ['index.html'.'index.htm']});const server = http.createServer((req, res) = > {
const done = finalhandler(req, res);
fileServer(req, res, done);
});
server.listen(3200.() = > {
console.log('[file server] running at port 3200.');
});
Copy the code
Create static/index.html file in curl directory:
$curl = $curl = $curl = $curl = $curl//localhost:3200
Copy the code
- Demonstrates an example of providing resource downloads in an HTTP service
const http = require('http');
const contentDisposition = require('content-disposition');
const finalhandler = require('finalhandler');
const serveStatic = require('serve-static');
// Initialize the file download service
const root = __dirname + '/static';
const fileServer = serveStatic(root, {
index: false.setHeaders: setHeaders,
});
// Set the response header to force the download
function setHeaders(res, path) {
res.setHeader('Content-Disposition', contentDisposition(path));
}
// Initialize the HTTP service
const server = http.createServer((req, res) = > {
const done = finalhandler(req, res);
fileServer(req, res, done);
});
server.listen(3200.() = > {
console.log('[file server] running at port 3200.');
});
Copy the code
Curl = curl = curl = curl = curl = curl
# will servestaticCurl -o./download. HTML HTTP: /download.//localhost:3200/index.html
Copy the code
- Used as Express middleware
const express = require('express');
const serveStatic = require('serve-static');
const root = __dirname + '/static';
const fileServer = serveStatic(root, {
index: ['index.html'.'index.htm']});const app = new express();
app.use(fileServer);
app.listen(3200.() = > {
console.log('[koa file server] running at port 3200.');
});
Copy the code
Source code analysis
The send library is implemented as an index.js file in the root directory. The core structure is to export a function:
// the index.js file in the root directory
'use strict'
var send = require('send')
/**
* Module exports.
* @public* /
module.exports = serveStatic
module.exports.mime = send.mime
function serveStatic (root, options) {}
Copy the code
Let’s look at the implementation of serveStatic:
function serveStatic (root, options) {
// The root path must be specified
if(! root) {throw new TypeError('root path required')}// Type check for root path values
if (typeofroot ! = ='string') {
throw new TypeError('root path must be a string')}// copy Parameters passed by the user
var opts = Object.create(options || null)
// fall-though The default value is true
varfallthrough = opts.fallthrough ! = =false
// default redirect The default value is true
varredirect = opts.redirect ! = =false
// headers listener
var setHeaders = opts.setHeaders
if (setHeaders && typeofsetHeaders ! = ='function') {
throw new TypeError('option setHeaders must be function')}// setup options for send
opts.maxage = opts.maxage || opts.maxAge || 0
opts.root = resolve(root)
// send a handler for the directory event
// Is used for further redirection when processing path-time file options
// This event allows users to customize folder path jump logic
var onDirectory = redirect
? createRedirectDirectoryListener()
: createNotFoundDirectoryListener()
// Returns the middleware function
return function serveStatic (req, res, next) {}
/ /...
}
Copy the code
This function first initializes some default parameters and then returns a middleware formatted function. There is nothing to say here, but one small tip to mention is the initialization technique for Boolean defaults:
// Set the default value to true
The default value is true as long as the user does not display the declaration argument as false
varfallthrough = opts.fallthrough ! = =false
// Set the default value to false
The default value is false as long as the user does not display the declaration parameter as true
var redirect = opts.redirect === true
Copy the code
Let’s take a closer look at the returned middleware function:
// Return middleware
return function serveStatic (req, res, next) {
// Processing requests is not a GET or HEAD scenario
if(req.method ! = ='GET'&& req.method ! = ='HEAD') {
// If fallThrough is true, the next middleware is executed directly next
if (fallthrough) {
return next()
}
// Otherwise respond directly to the 405 status notification that only GET or HEAD requests are allowed
res.statusCode = 405
res.setHeader('Allow'.'GET, HEAD')
res.setHeader('Content-Length'.'0')
res.end()
return
}
varforwardError = ! fallthroughvar originalUrl = parseUrl.original(req)
// Get the pathName path
var path = parseUrl(req).pathname
// make sure redirect occurs at mount
if (path === '/' && originalUrl.pathname.substr(-1)! = ='/') {
path = ' '
}
// instantiate send to get the flow
var stream = send(req, path, opts)
// Add processing logic for folder resources
stream.on('directory', onDirectory)
// If the user sets setHeaders, customize the response header function
if (setHeaders) {
stream.on('headers', setHeaders)
}
// add file listener for fallthrough
if (fallthrough) {
stream.on('file'.function onFile () {
// If it is a read file, set this variable to true
// This variable is used to check whether the file reads next error
forwardError = true})}// Listen for the wrong hook
stream.on('error'.function error (err) {
// If the user allows next error logic, or the error status code is greater than or equal to 500
// Next error
if(forwardError || ! (err.statusCode <500)) {
next(err)
return
}
next()
})
// Connect the stream to the RES stream, i.e. HTTP returns stream data
stream.pipe(res)
}
Copy the code
The main logic of this part:
- The non
GET | HEAD
The request of- This parameter is determined based on the configuration parameters
next()
Or response405
error
- This parameter is determined based on the configuration parameters
- instantiation
send
getsend
The instancestream
flow - add
send
The instancedirectory
The event- Determine redirection or response based on configuration parameters
404
error send
The library of the defaultdirectory
Logic is the response403
error
- Determine redirection or response based on configuration parameters
- Add headers events for send instances to allow users to customize the response headers
- Add error handling events for send instances
- If it is a file outflow error directly
next(err)
- If the error status code is greater than or equal to 500 directly
next(err)
- Otherwise, the value is determined based on the configuration parameters
next(err)
ornext
- If it is a file outflow error directly
stream.pipe(res)
Returns the stream data for the response
Finally see createRedirectDirectoryListener redirect logic:
/** * Create a directory listener that performs a redirect. That is, the redirectory implementation * in custom Send@private* /
function createRedirectDirectoryListener () {
return function redirect (res) {
/** * Call the hasTrailingSlash method inside the send library to determine if the path ends in '/'. * 404 */ when no resource is matched
if (this.hasTrailingSlash()) {
this.error(404)
return
}
// Redirection logic, redirection to path/, is basically the same as the send library implementation
// get original URL
var originalUrl = parseUrl.original(this.req)
// append trailing slash
originalUrl.path = null
originalUrl.pathname = collapseLeadingSlashes(originalUrl.pathname + '/')
// reformat the URL
var loc = encodeUrl(url.format(originalUrl))
var doc = createHtmlDocument(
'Redirecting'.'Redirecting to <a href="' + escapeHtml(loc) + '" >' + escapeHtml(loc) + '</a>'
)
// Set the redirection status code
res.statusCode = 301
// Set the relevant headers for redirection
res.setHeader('Content-Type'.'text/html; charset=UTF-8')
res.setHeader('Content-Length', Buffer.byteLength(doc))
res.setHeader('Content-Security-Policy'."default-src 'none'")
res.setHeader('X-Content-Type-Options'.'nosniff')
// Redirect to loC's address
res.setHeader('Location', loc)
res.end(doc)
}
}
Copy the code
The core logic of this redirection is to get the address path/ to redirect, and then set the response header to redirect:
// Set the redirection status code
res.statusCode = 301
// Set the relevant headers for redirection
res.setHeader('Content-Type'.'text/html; charset=UTF-8')
res.setHeader('Content-Length', Buffer.byteLength(doc))
res.setHeader('Content-Security-Policy'."default-src 'none'")
res.setHeader('X-Content-Type-Options'.'nosniff')
// Redirect to loC's address
res.setHeader('Location', loc)
res.end(doc)
Copy the code
conclusion
If you like this article ❤️❤️ 👍👍 👍👍 tweet ❤️❤️❤️ ha ~ ~ ~
I also recommend you read my other Nuggets articles:
- Explain the implementation principle of NodeJs static file hosting service in “Send” source code
- Explain the Node middleware model architecture in the source code of Connect
- NodeJS techniques in live-Server source code
- Ts Master: 22 examples to delve into Ts’s most obscure advanced type tools 👍 1.5K
- Here are 28 Canvas libraries that will make you scream “wow!” 👍 852
- In-depth explanation of VsCode advanced debugging and use skills in various scenarios
- Vue + TS +class+ annotation style development pit full guide
I am leng hammer, I and the front end of the story continues……