Front end er, when do you want to write an HTTP server?

Front end er, when do you want to write an HTTP server?

When you first came into contact with an engineering project, you saw that the project console was building. After a while, a URL address suddenly popped up. You click it to open the web page you just wrote. It's amazing.

When you connect to the back-end partner's interface, you bring the data, and the interface returns 500 errors to you; You go to the back end and the back end says it can't pass like this. You don't know why. Anyway, after changing according to what he said, it returns to 200 successfully.

Sometimes your request is inexplicably cross domain. The back end says you can handle it yourself, so you can find a solution. But why cross domain? You don't know how the backend is configured.

Finally, one day, you learn from the past and decide to change the past. You must set up an HTTP server by yourself and thoroughly sort out the twists and turns in it. From then on, you refuse to be fooled and refuse to be a big soldier who only listens to orders.

But then again, how to start?

Don't worry, it's all ready for you. Writing an HTTP server requires a back-end language. Needless to say, Node.js is naturally preferred.

Let's build an HTTP server based on the HTTP module of Node.js.

http module

An example of a super simple HTTP web server:

const http = require('http')

const server = http.createServer((request, response) => {
  response.statusCode = 200
  response.end('hello world')
})

server.listen(3000)

Introduced here http Module, which provides the createServer method, passes in a callback function and creates a server.

Now write the code into index.js and run it super simply:

$ node index.js

Open the browser and enter http://localhost:3000 , you can see the hello world displayed on the web page.

Code analysis

The parameter of the http.createServer method is a callback function, which has two parameters -- they are the core of the HTTP server.

The first parameter is the request object request, and the second parameter is the response object response. You can think of them as two bags, one containing request related data and the other containing response related operations.

The request contains the detailed request data, that is, the data passed from our front-end calling interface. Through it, you can get the request header, request parameters, request methods, etc.

Response is mainly used to respond to related settings and operations. What is response? When I receive the request from the client, I can set the status code to 200 and return it to the front-end data; Or set the status code to 500 and return it to the front-end error.

In a word, what is returned by the calling interface is determined by the response.

In fact, createServer returns a EventEmitter Therefore, the above wording is equivalent to this:

const http = require('http')
const server = http.createServer()

server.on('request', (request, response) => {
  response.statusCode = 200
  response.end('hello world')
}).listen(3000)

request parsing

The relevant data of the user initiated request is contained in the request object.

These data include common request methods, request headers, URLs, request bodies, and so on.

const { method, url, headers } = request

Method means that the request method can be used directly. headers returns the request header object, which is also easy to use:

const { headers } = request
const userAgent = headers['user-agent'] // Request headers are all lowercase letters

Only the url string is not easy to parse. It contains protocol, hostname, path, query and so on.

Fortunately, Node.js provides url and querystring modules to parse url strings.

URL resolution

Let's take a look at an example of the url module:

const url = require('url') // Parse url string
var string = 'http://localhost:8888/start?foo=bar&hello=world'

var url_object = url.parse(string)
// { protocol: 'http:', host:'localhost:8888', pathname: '/start', query: 'foo=bar&hello=world' }

You see, the URL module can split a complete URL address string into an object containing the attributes of each part.

But the beauty is flawed. Other parts have been resolved, except that query is still a string.

query needs secondary parsing. What shall I do? At this time, the second module querystring appears:

const querystring = require('querystring') // Parse query string
var string = 'http://localhost:8888/start?foo=bar&hello=world'

var url_object = url.parse(string) // { query: 'foo=bar&hello=world' }
var query_object = querystring.parse(url_object.query)
// { foo: 'bar', hello: 'world' }

That's perfect. With the combination of url + querystring, you can completely parse your URL.

Request body resolution

For POST or PUT requests, we need to receive the data of the request body.

The request body here is special. It is not the data transmitted at one time, but the data is transmitted by Stream. Therefore, it needs to receive a little bit by listening to data and end events.

The acquisition method is as follows:

server.on('request', (request, response) => {
  let body = []
  request.on('data', chunk => {
    // The chunk here is a Buffer
    body.push(chunk)
  })
  request.on('end', () => {
    body = Buffer.concat(body)
  })
  console.log(body.toString())
})

response settings

When the server receives a client request, it should set how to respond to the client through response.

Response settings mainly include status code, response header and response body.

The first is the status code, such as 404:

response.statusCode = 404

Another is the response header:

response.setHeader('Content-Type', 'text/plain')

Finally, the response body:

response.end('Data not found')

These three parts can also be combined:

response
  .writeHead(404, {
    'Content-Type': 'text/plain',
    'Content-Length': 49
  })
  .end('Data not found')

Send http request

In addition to accepting the client's request, the http module can also send the request as a client.

Sending an http request means requesting other interfaces to obtain data in Node.js.

Sending a request is mainly realized through the http.request method.

GET

The following is a simple example of sending a GET request:

const http = require('http')
const options = {
  hostname: 'nodejs.cn',
  port: 80,
  path: '/learn',
  method: 'GET'
}

const req = http.request(options, res => {
  console.log(`Status code: ${res.statusCode}`)
  res.on('data', d => {
    process.stdout.write(d)
  })
  res.on('end', () => {})
})

req.on('error', error => {
  console.error(error)
})

req.end()

After sending the request using http.request, it must be displayed that the call req.end() is completed.

POST

It is basically the same as the above GET request. The difference is that it depends on how the request body is transmitted:

const http = require('http')
const options = {
  hostname: 'nodejs.cn',
  port: 80,
  path: '/learn',
  method: 'POST'
}

const body = {
  sex: 'man',
  name: 'ruims'
}

const req = http.request(options, res => {
  console.log(`Status code: ${res.statusCode}`)
  res.on('data', d => {
    process.stdout.write(d)
  })
  res.on('end', () => {})
})

req.on('error', error => {
  console.error(error)
})

req.write(JSON.stringify(body)) // Writing method of passing body parameter

req.end()

Weird place

See here, if you don't understand nodejs deeply, you may find several strange places.

For example, under normal circumstances, the body parameter passed in a POST request may be as follows:

var body = { desc: 'Request body parameters' }
var req = http.request({
  path: '/',
  method: 'POST',
  data: body
})

The correct posture mentioned above is as follows:

var body = { desc: 'Request body parameters' }
var req = http.request({
  path: '/',
  method: 'POST'
})
req.write(JSON.stringify(body))

The same is true for the above request body. You can't get it directly through request.body. You have to do this:

let body = []
request.on('data', chunk => {
  body.push(chunk)
})
request.on('end', () => {
  body = Buffer.concat(body)
})

These should be the most confusing places for everyone to understand the http module. In fact, this is not the difficulty of http, but the unique syntax of Stream stream in Node.js.

In fact, both request and response, the core of http module, belong to Stream, one is readable Stream and the other is writable Stream.

Therefore, to fully understand the http module, you also need to have an in-depth understanding of Stream flow.

summary

This paper builds a simple HTTP server based on the most basic HTTP module, and realizes simple receiving and sending requests.

However, real application scenarios generally don't match like this. The community has mature and stable express The framework is more suitable for writing Node.js services; To send a request, you can use the one we are most familiar with axios ——- yes, axios can also be used in Node.js.

But you may not know that the core functions of express and axios are based on http modules.

Therefore, the foundation is very important. If the foundation is not firm, the earth will shake. Master the http module, even if you see Stream in express   The usage of is not unknown, so.

That's all for this article. In the next article, let's continue to explore Stream flow. Remember to pay attention to me.

Previous highlights

book special column It will output articles on front-end engineering and architecture for a long time, which have been published as follows:

If you like my article, please praise and support me! Also welcome to my column.

Statement: This article is original. If you need to reprint it, please contact wechat ruidoc for authorization.

Tags: Javascript node.js http

Posted on Mon, 29 Nov 2021 21:59:03 -0500 by wigz01