前言

从浏览器向服务器提交数据时,常见有表单提交,json 字符串提交和普通字符串提交,不同情况需要附带不同的请求头信息,告诉服务器客户端可以直接解析的数据格式,如果发送的数据为 json 字符串,后两种类型都可以成功发送到服务器,只是加上请求头信息服务器更容易判断该以什么格式返回数据。

querystring 模块解析请求体

get 请求中,我们可以通过 url 模块的 parse 方法来解析,如果是带有请求体的请求类型,如 postput 我们应该使用 querystring 模块的 parse 方法将请求体中的数据解析成对象,在这个方法中有三个参数。

  • str:要解析的查询字符串;
  • sep:查询字符串中键值对之间的分隔符,默认为 &
  • eq:查询字符串中的键与值的分隔符,默认为 =
/* 文件:querystring-test.js */
const querystring = require('querystring');

const query1 = 'name=pandashen&age=27';
const query2 = 'name*pandashen!&age*27';

const params1 = querystring.parse(query1);
const params2 = querystring.parse(query2, '!&', '*');

console.log(params1); // { name: 'pandashen', age: '27'}
console.log(params2); // { name: 'pandashen', age: '27'}

querystring 也是很常用的模块,就在这里多说几句关于 querystring 模块常用方法 parse 的实现。

/* 文件:my-querystring.js */
exports.parse = (str, sep = '&', eq = '=') => {
  // 存储解析出键值的对象
  const query = {};

  // 先将查询字符串切割成 [k=v, k=b] 的形式
  const fields = str.split(sep);

  // 循环将每一项切割成 k 和 v 并存入 queryObj 中
  fields.forEach(field => {
    const [key, value] = field.split(eq);
    query[key] = value;
  });

  // 返回 query 对象
  return query;
};

服务器的实现

向服务器发送请求的请求头为 Content-Type,表单提交、json 和字符串作为请求体时,在 Content-Type 中对应的值分别为 application/x-www-form-urlencodedapplication/jsontext/plain

其中 text/plainAjax 的默认提交方式,我们在服务器中针对上面这几种类型的请求头和 get 请求做处理,将发来的数据再次返回客户端。

/* 文件:server.js */
const http = require('http');
const url = require('url');
const querystring = require('querystring');

const server = http.createServer((req, res) => {
  // 获取 get 请求参数
  const { query } = url.parse(req.url, true);
  // 获取数据类型请求头
  const type = req.headers['content-type'];
  // 接收数据
  const buffers = [];

  res.on('data', data => buffers.push(data));
  res.on('end', () => {
    // 合并数据并设置默认响应头和返回数据
    let data = Buffer.concat(buffers).toString();
    let contentType = 'application/json';

    // 判断是否为 get 请求,是则直接返回解析后的数据,不是则判断请求类型
    if (req.method.toLowerCase() === 'get') {
      data = JSON.stringify(query);
    } else {
      // 判断请求数据类型并做相应处理
      if (type === 'application/x-www-form-urlencoded') {
        data = JSON.stringify(querystring.parse(data));
      } else if (type === 'application/json') {
        data = JSON.stringify(JSON.parse(data));
      } else {
        contentType = 'text/plain';
      }
    }

    // 设置响应头并返回数据
    res.setHeader('Content-Type', contentType);
    res.end(data);
  });
});

server.listen(3000, () => {
  console.log('server start 3000');
}

当请求类型为 get,将查询字符串通过 url 模块解析后再处理成字符串返回客户端。

当请求类型为 post,设置默认响应头为 application/json,如果是表单提交,请求体中的内容为查询字符串格式,使用 querystring 解析后再使用 JSON.stringify 处理成字符串返回,如果是 json,则使用 JSON.parse 解析,并使用 JSON.stringify 处理成字符串返回,如果是默认值 text/plain,则设置响应头的值为 text/plain 并将读取的结果直接返回。

使用客户端进行测试

在这里我们为了方便就不用浏览器访问了(需要创建各种不同类型提交的页面),因为上面的服务器代码比较简单,只处理了数据,并没有处理静态文件请求,所以我们通过 Node.js 来实现客户端。

get 请求

/* 文件:get.js */
const http = require('http');

const config = {
  host: 'localhost',
  port: 3000,
  path: '/?name=pandashen&age=27'
};

// 发送 get 请求
http.get(config, res => {
  // 接收服务器返回的数据
  const buffers = [];
  res.on('data', data => buffers.push(data));
  res.on('end', () => {
    const data = Buffer.concat(buffers).toString();
    console.log(data);
  });
});

启动服务器 server.js,通过命令行执行 node get.js 查看命令窗口中输出的结果。

post 请求表单提交

/* 文件:post-from.js */
const http = require('http');

const config = {
  host: 'localhost',
  port: 3000,
  method: 'post'
  headers: {
    'Content-type': 'application/x-www-form-urlencoded'
  }
};

http.request(config, res => {
  // 接收服务器返回的数据
  const buffers = [];
  res.on('data', data => buffers.push(data));
  res.on('end', () => {
    const data = Buffer.concat(buffers).toString();
    console.log(data);
  });
}).end('name=pandashen&age=27');

启动服务器 server.js,通过命令行执行 node post-form.js 查看命令窗口中输出的结果。

post 请求 json 字符串数据

/* 文件:post-json.js */
const http = require('http');

const config = {
  host: 'localhost',
  port: 3000,
  method: 'post',
  headers: {
    'Content-Type': 'application/json'
  }
};

http.request(config, res => {
  // 接收服务器返回的数据
  const buffers = [];
  res.on('data', data => buffers.push(data));
  res.on('end', () => {
    const data = Buffer.concat(buffers).toString();
    console.log(data);
  });
}).end('{ name: pandashen, age: 27 }');

启动服务器 server.js,通过命令行执行 node post-json.js 查看命令窗口中输出的结果。

post 请求普通字符串数据

/* 文件:post-string.js */
const http = require('http');

const config = {
  host: 'localhost',
  port: 3000,
  method: 'post',
  headers: {
    'Content-Type': 'text/plain'
  }
};

http.request(config, res => {
  // 接收服务器返回的数据
  const buffers = [];
  res.on('data', data => buffers.push(data));
  res.on('end', () => {
    let data = Buffer.concat(buffers).toString();
    console.log(data);
  });
}).end('pandashen27');

启动服务器 server.js,通过命令行执行 node post-string.js 查看命令窗口中输出的结果。

总结

通过本篇的内容可以了解 HTTP 在数据传输中的类型,即请求头类型,服务端通过请求头类型可以返回客户端可以直接解析的数据,上面的几种类型只是向服务器提交数据的最常见类型,涵盖表单提交和 Ajax 等(在上传文件时还存在二进制传输)。