This is the 15th day of my participation in Gwen Challenge

Introduction to the

Http-errors are mainly used by back-end frameworks such as Express and KOA to facilitate the creation of HTTP exception status. Simple use is as follows:

var createError = require('http-errors')
var express = require('express')
var app = express()
const port = 3001

app.use(function (req, res, next) {
  console.log(req)
  if(! req.user)return next(createError(401.'Please login to view this page.'))
  next()
})

app.listen(port, () = > {
  console.log(`Example app listening at http://localhost:${port}`)})Copy the code

Rely on

The external dependencies of HTTP-errors include depD, setProtoTypeof, Statuses, inherits, and toIdentifier.

var deprecate = require('depd') ('http-errors')
var setPrototypeOf = require('setprototypeof')
var statuses = require('statuses')
var inherits = require('inherits')
var toIdentifier = require('toidentifier')
Copy the code

depd

Depd is used to output prompts and implement backward compatibility operations, as shown below:

var deprecate = require('depd') ('LvLin-test')
deprecate('Hello, I am LvLin.') // Output the prompt

function sayHello () {
  console.log('Hello world! ')}SayHello is actually run with a prompt
const sayHi = deprecate.function(sayHello, 'sayHi; use "sayHello" instead')
sayHi()
Copy the code

The effect is shown below:

statuses

Statuses is a library of tools that handle HTTP status codes. Note that http-Errors uses statuses lower than version 2.0, as some apis are no longer supported in version 2.x.

"statuses": "> = 1.5.0 < 2".Copy the code

A simple use of statuses looks like this:

status[404] // => 'Not Found'
status['Not Found'] / / = > 404
status.codes  // Get all status codes
/ / /
// 100, 101, 102, 103, 200, 201, 202, 203, 204,
// 205, 206, 207, 208, 226, 300, 301, 302, 303,
// 304, 305, 306, 307, 308, 400, 401, 402, 403,
// 404, 405, 406, 407, 408, 409, 410, 411, 412,
// 413, 414, 415, 416, 417, 418, 421, 422, 423,
// 424, 425, 426, 428, 429, 431, 451, 500, 501,
// 502, 503, 504, 505, 506, 507, 508, 509, 510,
/ / 511
// ]
Copy the code

The statuses source code can be looked at briefly, the related explanation see the note:

'use strict'

// Introduce the status code mapping table
/ / {
// "100": "Continue",
// "101": "Switching Protocols",
// "102": "Processing",
// "103": "Early Hints",
/ /...
// }
var codes = require('./codes.json')

module.exports = status

// status.status_codes holds the reason phrase mapping for all status codes
status.STATUS_CODES = codes

// status.codes store all status codes
status.codes = populateStatusesMap(status, codes)

// The status code associated with redirection
status.redirect = {
  300: true.301: true.302: true.303: true.305: true.307: true.308: true
}

// The response message does not have a body status code
status.empty = {
  204: true.205: true.304: true
}

// The request cannot be processed temporarily and the request status code needs to be retried
status.retry = {
  502: true.503: true.504: true
}

// Bind status codes and cause phrases to statuses
function populateStatusesMap (statuses, codes) {
  var arr = []

  Object.keys(codes).forEach(function forEachCode (code) {
    var message = codes[code]
    var status = Number(code)

    // Status codes and cause phrases are bound to statuses
    // All lowercase reason phrases are also considered
    statuses[status] = message
    statuses[message] = status
    statuses[message.toLowerCase()] = status

    // Save the status code to return to status.codes
    arr.push(status)
  })

  return arr
}

// Accept a code that can only be a number or a string
// If it is a number: If it is a supported status code, return the number, otherwise throw an exception
// If string: 1, try to convert to a number, if not NaN, determine whether it is a supported status code, return the number or throw an exception
// 2. Check whether there is a matching cause phrase and return the corresponding status code or throw an exception
function status (code) {
  if (typeof code === 'number') {
    if(! status[code])throw new Error('invalid status code: ' + code)
    return code
  }

  if (typeofcode ! = ='string') {
    throw new TypeError('code must be a number or string')}/ / '403'
  var n = parseInt(code, 10)
  if (!isNaN(n)) {
    if(! status[n])throw new Error('invalid status code: ' + n)
    return n
  }

  n = status[code.toLowerCase()]
  if(! n)throw new Error('invalid status message: "' + code + '"')
  return n
}

Copy the code

setprototypeof

Setprototypeof is an Object. setProtoTypeof shim library, cross-platform, compatible with IE8.

Take a look at the source code implementation:

'use strict'
/* eslint no-proto: 0 */
// Object. SetPrototypeOf is preferred
// Determine if using __proto__ can change the prototype chain, and if so use setProtoOf
// If not use mixinProperties
module.exports = Object.setPrototypeOf || ({ __proto__: []}instanceof Array ? setProtoOf : mixinProperties)

// You can modify the prototype chain directly by __proto__
function setProtoOf (obj, proto) {
  obj.__proto__ = proto
  return obj
}

// Cannot modify the prototype chain with __proto__
function mixinProperties (obj, proto) {
  for (var prop in proto) {
      // Find attributes that do not already exist in this object and bind them to the object
    if (!Object.prototype.hasOwnProperty.call(obj, prop)) {
      obj[prop] = proto[prop]
    }
  }
  return obj
}
Copy the code

inherits

Inherits is used to implement prototype inheritance of objects, simply as follows:

function Base() { 
    this.name = 'base'; 
    this.base = 1991; 
} 
function Sub() { 
    this.name = 'sub'; 
}
inherits(Sub, Base); 

let objSub = new Sub;
objSub.base / / 1991
Copy the code

Take a look at the source:

// inherits.js
try {
   // If the node environment is present, use util.inherits
  var util = require('util');
  /* istanbul ignore next */
  if (typeofutil.inherits ! = ='function') throw ' ';
  module.exports = util.inherits;
} catch (e) {
  // Use inherits_browser.js if you are in a browser environment
  /* istanbul ignore next */
  module.exports = require('./inherits_browser.js');
}

// inherits_browser.js
if (typeof Object.create === 'function') {
  // If object.create is supported, use object.create to implement parasitic combinatorial inheritance
  module.exports = function inherits(ctor, superCtor) {
    if (superCtor) {
      ctor.super_ = superCtor
      ctor.prototype = Object.create(superCtor.prototype, {
        constructor: {
          value: ctor,
          enumerable: false.writable: true.configurable: true}}}}; }else {
  // Implement parasitic combinatorial inheritance using empty constructors
  module.exports = function inherits(ctor, superCtor) {
    if (superCtor) {
      ctor.super_ = superCtor
      var TempCtor = function () {}
      TempCtor.prototype = superCtor.prototype
      ctor.prototype = new TempCtor()
      ctor.prototype.constructor = ctor
    }
  }
}

Copy the code

toIdentifier

ToIdentifier is used to convert a string into a canonical variable name.

function toIdentifier (str) {
  return str
    .split(' ') // Split the string into arrays by Spaces
    .map(function (token) {
      // Capitalize each word
      return token.slice(0.1).toUpperCase() + token.slice(1)
    })
    .join(' ') // Put all the words back together again
    .replace(/[^ _0-9a-z]/gi.' ')  // Remove special characters
}
Copy the code

Http-errors source code analysis

Let’s start with a few simple utility functions.

// Categorize the status codes according to the starting number, for example, 401, 402 codeClass is 400
function codeClass (status) {
  return Number(String(status).charAt(0) + '00')}// Get the class name from the name identifier, ending with Error,
function toClassName (name) {
  return name.substr(-5)! = ='Error'
    ? name + 'Error'
    : name
}

// Change the name of the function
function nameFunc (func, name) {
  var desc = Object.getOwnPropertyDescriptor(func, 'name')

  if (desc && desc.configurable) {
    // If modifiable, modify the function name with Object.defineProperty
    desc.value = name
    Object.defineProperty(func, 'name', desc)
  }
}
Copy the code

Create. HttpError is an abstract class for inheritance and cannot be called directly.

module.exports.HttpError = createHttpErrorConstructor() // Generate an abstract base class

function createHttpErrorConstructor () {
  // This is an abstract class and cannot be called directly, just for inheritance
  // Throw an exception if called directly
  function HttpError () {
    throw new TypeError('cannot construct abstract class')
  }
  inherits(HttpError, Error)
  return HttpError
}
Copy the code

CreateError. IsHttpError Is used to check whether the instance is of the HttpError type.

module.exports.isHttpError = createIsHttpErrorFunction(module.exports.HttpError)

function createIsHttpErrorFunction (HttpError) {
  return function isHttpError (val) {
    if(! val ||typeofval ! = ='object') {
      return false
    }
    // Return true if inherited from HttpError
    if (val instanceof HttpError) {
      return true
    }
	// Custom error type condition created with createError()
    return val instanceof Error &&
      typeof val.expose === 'boolean' &&
      typeof val.statusCode === 'number' && val.status === val.statusCode
  }
}
Copy the code

At initialization, constructors for each status code exception class are created and bound to createError.

// Bind error constructor to createError for easy call, such as new createError.notFound ()
populateConstructorExports(module.exports, statuses.codes, module.exports.HttpError)

function populateConstructorExports (exports, codes, HttpError) {
  codes.forEach(function forEachCode (code) {
    var CodeError
    var name = toIdentifier(statuses[code])

    switch (codeClass(code)) {
      case 400: // 4xx status code, which provides the constructor of the client exception class
        CodeError = createClientErrorConstructor(HttpError, name, code)
        break
      case 500: // 5xx status code, which provides the constructor of the server exception class
        CodeError = createServerErrorConstructor(HttpError, name, code)
        break
    }

    if (CodeError) {
      // createError[404] === createError.NotFound
      // Code and name correspond to the same constructor
      exports[code] = CodeError
      exports[name] = CodeError
    }
  })

  // Compatible with older versions of I'mateapot
  exports["I'mateapot"] = deprecate.function(exports.ImATeapot,
    '"I\'mateapot"; use "ImATeapot" instead')}Copy the code

Now look at how the constructors for the client-side and server-side exception classes are generated.

CreateServerErrorConstructor return ServerError class constructor.

function createServerErrorConstructor (HttpError, name, code) {
  var className = toClassName(name)

  function ServerError (message) {
    // Create an Error instance, with the Error described as the passed value or
    varmsg = message ! =null ? message : statuses[code]
    var err = new Error(msg)

    / / capture structure points stack trace, use http://nodejs.cn/api/errors.html#errors_error_capturestacktrace_targetobject_constructoropt
    Error.captureStackTrace(err, ServerError)

    // err.__proto__ = ServerError.prototype
    // Make err instance of the ServerError class
    setPrototypeOf(err, ServerError.prototype)

    // Redefine the error description of err
    Object.defineProperty(err, 'message', {
      enumerable: true.configurable: true.value: msg,
      writable: true
    })

    // Redefine the name of err
    Object.defineProperty(err, 'name', {
      enumerable: false.configurable: true.value: className,
      writable: true
    })

    return err
  }

  The ServerError class inherits from the HttpError class
  inherits(ServerError, HttpError)
  // Rename the constructor to name + 'Error' to avoid duplication
  nameFunc(ServerError, className)

  ServerError.prototype.status = code
  ServerError.prototype.statusCode = code
  ServerError.prototype.expose = false

  return ServerError
}
Copy the code

CreateClientErrorConstructor return ClientError class constructor, its implementation and ServerError basic consistent.

function createClientErrorConstructor (HttpError, name, code) {
  var className = toClassName(name)

  function ClientError (message) {
    // Create the Error class instance
    varmsg = message ! =null ? message : statuses[code]
    var err = new Error(msg)

    / / capture structure points stack trace, use http://nodejs.cn/api/errors.html#errors_error_capturestacktrace_targetobject_constructoropt
    Error.captureStackTrace(err, ClientError)

    // err.__proto__ = ClientError.prototype
    // Make err an instance of ClientError class
    setPrototypeOf(err, ClientError.prototype)

    // Redefine the error description of err
    Object.defineProperty(err, 'message', {
      enumerable: true.configurable: true.value: msg,
      writable: true
    })

    // Redefine the name of err
    Object.defineProperty(err, 'name', {
      enumerable: false.configurable: true.value: className,
      writable: true
    })

    return err
  }
  // The ClientError class inherits from the HttpError class
  inherits(ClientError, HttpError)
  // Rename the constructor to name + 'Error' to avoid duplication
  nameFunc(ClientError, className)

  ClientError.prototype.status = code
  ClientError.prototype.statusCode = code
  ClientError.prototype.expose = true

  return ClientError
}
Copy the code

Finally, look at the implementation of the createError() method, which is explained in the comment:

// There are two ways to call, but since arguments are omitted, there are more arguments to consider
// createError([status], [message], [properties])
// createError([status], [error], [properties])
function createError () {
  var err
  var msg
  var status = 500
  var props = {}

  // Walk through the parameters to determine each parameter type
  for (var i = 0; i < arguments.length; i++) {
    var arg = arguments[i]
    // If it is inherited from Error, it is the corresponding Error in the argument list
    if (arg instanceof Error) {
      err = arg
      status = err.status || err.statusCode || status
      continue
    }

    switch (typeof arg) {
      case 'string': // Message in the parameter list
        msg = arg
        break
      case 'number': // Status in the parameter list
        status = arg
        if(i ! = =0) { // If status is not the first argument, an exception is reported
          deprecate('non-first-argument status code; replace with createError(' + arg + ',...). ')}break
      case 'object': // If it is object, it is the properties in the parameter list
        props = arg
        break}}// Only 4XX and 5XX exceptions are supported
  if (typeof status === 'number' && (status < 400 || status >= 600)) {
    deprecate('non-error status code; use only 4xx or 5xx status codes')}// If status does not meet the criteria, status is set to 500
  if (typeofstatus ! = ='number'| | (! statuses[status] && (status <400 || status >= 600))) {
    status = 500
  }

  // Get the constructor for the status code. The constructor for 400 or 500 is already mounted to createError
  var HttpError = createError[status] || createError[codeClass(status)]

  if(! err) {// Create an exception class instance
    err = HttpError
      ? new HttpError(msg)
      : new Error(msg || statuses[status])
    / / / / capture stack trace structure damage concrete using http://nodejs.cn/api/errors.html#errors_error_capturestacktrace_targetobject_constructoropt
    Error.captureStackTrace(err, createError)
  }

  if(! HttpError || ! (errinstanceofHttpError) || err.status ! == status) { err.expose = status <500
    err.status = err.statusCode = status
  }

  for (var key in props) {
    if(key ! = ='status'&& key ! = ='statusCode') {
      err[key] = props[key]
    }
  }

  return err
}
Copy the code

The relevant data

One NPM package per day: HTTP-errors

HTTP errors – the source code

Nodejs – depd source code

Toidentifier source

Statuses source

Inherits the source code

Setprototypeof source

The last

If the article is helpful to you, give it a thumbs up