Web 应用程序前端 ( 客户端 ) 与后端 ( 服务器端 ) 是如何进行数据交换的? 即: 客户端的数据怎么传到服务器端, 服务器端的数据又是怎么传到客户端? 这是初学 Web 应用程序开发时经常遇到的一个坎.

今天, 我们就来细聊一下这个问题.

本文假设你已经对前端技术 ( HTML / CSS / JavaScript ) 有所了解. 若假设不成立, 请先学习前述预备知识.

1. 术语

为统一认识, 先对一些基本术语作一些定义和解释, 不一定严谨, 大家明白就行…

  • 客户端

    用户所在的那一端. 通常用户使用电脑/手机 或 其它安装有浏览器 ( IE / Chrome / Firefox / …) 的设备来访问我们的网站, 所以我们把浏览器端称作客户端.

    狭义的客户端可等同于浏览器. 广义而言, 微信小程序, Web App, Hybrid App 都可理解为客户端.

    Web App - 可简单理解为适配手机的网站, 人称微网站.

    Hybrid App - 混合/混生 App, 大部分功能由网页开发技术实现, 同时可借助宿主环境的提供的API与手机操作系统 (硬件系统) 进行交互.

  • 服务器端

    向前端(客户端)提供服务的那一端.

    服务器端和前端主要交换两类资源:

    (1) 静态资源: HTML / CSS / JavaScript / 图片…. 用户打开/刷新网页的时候从服务器下载到客户端.

    (2) 数据: 就是”数据”, 比如用户填写的表单中的数据. 通常是双向的: 服务器 ←→ 客户端

    对于传统的网站 和 Web App 而言, 服务器须前客户端提供上述2类资源

    对于微信小程序 和 Hybrid App 而言, 在程序安装阶段, 其大部份静态资源已事先打包安装于用户的手机上了, 所以服务器端主要负责与客户端交换数据.

  • HTTP

    超文本传输协议 ( Hypertext Transfer Protocol ) , 广泛应用于网站客户端与服务器端之间进行资源交换的传输协议.

    在没有特别要求的情况下, 通常使用 HTTP 进行前后端通信. 若对信息安全有要求, 可使用 HTTPS 协议.

    微信小程序正式发布版必须使用 HTTPS 协议. 开发环境下可使用 HTTP ( 要设置一下 ).

2. 准备开始

在正式开始之前再交待两句.

前/后端通信自然涉及到 前端程序后端程序 的编写.

相对而言, 前端技术比较单一, 掌握 HTML / CSS / JavaScript 基本就可以通吃了.

而后端技术种类繁多, 喜欢 Java 的可以用 J2EE, 喜欢 PHP 的就用 PHP, 要是钟爱 JavaScript 也可以基于 Node.js 来实现后端程序. 或者你喜欢微软的技术, 那还可以选择 ASP / ASP.NET . 当然, 想装个 B 的话, 用 C 语言来实现后端程序也完全可以. 一般而言, 在一个项目里, 选用一种后端技术即可, 没必要搞得那么花哨.

本文以一个小例子为示例: 前端向后端发送2个数字 ( a 和 b ), 后端接收到后, 回应前端 a + b 的结果.

3. 客户端实现

客户端向服务器端传送数据, 通常有 3 种方式, 下面分别举例.

你可以先其中一种方式来实验, 成功后再实验其它方式.

3.1 在 URL 中携带数据

新建一个名为 sum-1.html 的文件, 写入如下代码, 保存.

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>sum-1</title>
</head>
<body>
<a href="/sum.action?a=3&b=5">求 3 + 5 = ?</a>
</body>
</html>

上述代码中第 8 行是关键.

第 8 行的超链接 href 属性指定了此超链接点击后将转至 sum.action , 同时携带2个参数 a = 3, b = 5

在浏览器中打开 sum-1.html 效果如下:

sum-1.html显示效果

3.2 使用表单提交数据

新建一个名为 sum-2.html 的文件, 写入如下代码, 保存.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>sum-2</title>
</head>
<body>
<form action="/sum.action">
a <input name="a" type="number">
b <input name="b" type="number">
<button type="submit">求和</button>
</form>
</body>
</html>

上述代码中第 8 ~ 12 行在网页中放入了一个表单 ( form ), form 标签的 action 属性指向 "/sum.action" , 表明表单提交后数据将传输至 “/sum.action” 这个服务器端地址.

表单中有 3 个元素:

第 9 行 和 第 10 行使用 <input> 标签分别放入了两个输入框, 注意 input 标签中的 name 属性分别为 a 和 b, 它们是参数的名字, 在服务器端将使用这两个名字 ( a 和 b ) 取得前端传来的参数值.

第 11 行是一个 “提交按钮”, 注意其 type 属性为 “submit”, 点击即会提交表单, 同时携带2个参数 a 和 b.

在浏览器中打开 sum-2.html 效果如下:

sum-2.html显示效果

3.3 使用 Ajax 方式提交数据

Ajax 是一种异步的数据通信方式, 在如今的网站开发中广泛使用. 关于 Ajax 的优势本文不表, 大家可以查阅其它资料, 或自己慢慢体会.

现在先跟着往下做…

新建一个名为 sum-3.html 的文件, 写入如下代码, 保存.

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<title>sum-3</title>
<meta charset="UTF-8">
<script src="http://libs.baidu.com/jquery/1.11.1/jquery.min.js"></script>
<script src="sum-3.js"></script>
</head>
<body>
<button type="button" onclick="doSum()">求和</button>
</body>
</html>

第 6 行, 引入 jQuery 库. 现在的浏览器本身都是支持 Ajax 技术的, 只是直接使用有点麻烦. 这里使用 jQuery, 它对Ajax 进行了封装, 使用起来比较简单.

jQuery 是什么鬼? 看这里: jQuery入门精要

本例中直接使用了 百度 CDN 提供的 jQuery 源, 省去了下载 jQuery 的麻烦, 但因为是使用的 Internet 上的资源, 所以调试时要记得接入Internet.

第 7 行引入我们自己的 JavaScript 代码文件 ( 一会儿创建 ).

第 10 行在页面上放置了一个 “普通按钮”, 点击此按钮将提交数据到服务器端. 注意此按钮的 type 属性无须设置为 submit. 按钮的 onclick 属性指明如果用户点击了此按钮, 将触发 doSum 这个函数的执行.

接着, 在与 sum-3.html 相同的文件夹下创建一个名为 sum-3.js 的文件. 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
function doSum () {
$.ajax({
url : '/sum.action', // 发送数据的目的地
dataType : 'text', // 服务器端返回的数据格式(要与服务器端约定好)
data : { a: 3, b: 5 }, // 向服务器端传送的数据
beforeSend : function() { // 向服务器端发送数据前自动执行的回调函数
alert('我要准备发送数据到服务器端了哦~');
},
success : function(result) { // 成功接收到服务器端回传数据后自动执行的回调函数
alert(result);
}
});
}

上述代码在 sum-3.js 中定义了一个函数 ( doSum ), 当点击页面中的 “求和” 按钮时将触发此函数的执行.

第 2 ~ 12 行使用 jQuery 封装的 Ajax 方法向服务器端发送数据.

第 3 行指定数据将要发送到哪里, 与 3.1 节第 8 行, 以及 3.2 节第 8 行指定的目的地相同.

第 9 ~ 11 行是一个 回调函数 , 当服务器端回传的计算结果会被放在第 9 行的 result 参数里, 接着在第 10 行以消息框的形式显示得到的计算结果.

在浏览器中打开 sum-3.html 的效果就不附图了, 自己试吧!

如果一切正常, 在浏览器中打开页面 sum-3.html, 点击其中的 “求和” 按钮应弹出消息框, 内容为 “我要准备发送数据到服务器端了哦~”. 如果 “不正常”, 请检查如下几个方面:

(1) sum-3.html 中第 6 行写对了吗? 你的电脑接入 Internet 了吗? 检查这一点, 确保正确引入了 jQuery 库

(2) sum-3.html 中第 7 行所引入外部 js 文件 ( sum-3.js ) 是否在正确的文件夹下, 名字取对了吗?

================================

如果你 3.1 ~ 3.3 节都跟着做了, 那么应该有如下图所示的这些文件. 注意它们是在一起的 ( 同一个文件夹下 )

第3节所有源代码

算了, 以防万一, 还是把 源代码 附上吧, 有需要的自取. 不过还是强烈建议自己敲代码!

至此, 我们以 3 种不同的方式实现了客户端的程序, 算是完成了一半, 接下来还需要实现服务器的程序, 并正确部署, 这样客户端发送的数据才能正确被服务器端接收到, 客户端也才能正确接收到服务器端回应的数据. 即: 实现真正实现客户端与服务器端的信息交流.

4. 服务器端实现

如第 2 节中所述, 实现服务器端的技术有很多种, 具体选用哪一种, 看你喜欢.

本文将简介 2 种不同的技术实现: J2EE 和 Node.js.

如果你学过 Java, 可以先试试第 1 种方式. 如果没学过, 本博客中有 “Java 入门精要” 教程 :)

如果想试试使用 JavaScript 进行服务器端编程, 那可以自行传送到 4.2 节.

4.1 使用 J2EE 实现

关于 J2EE 开发环境的搭建, 可参看 IntelliJ IDEA + Tomcat 搭建Web应用程序开发环境 , Eclipse + Tomcat 搭建 Web 应用程序开发环境, 本文使用的是 IntelliJ IDEA + Tomcat.

4.1.1 创建 Web 项目

打开 IntelliJ IDEA , 选择菜单 File → New → Project … 创建一个 Web 应用程序项目.

New Project窗口

注意上图中红色框的两个地方.

友情提醒: Tomcat 的版本应与 JDK 版本兼容 !!!

Next …

在接下来的窗口中随意输入一个你中意的项目名称 ( Project Name, 我写的是 “example427” )

Finish.

创建好的项目的目录结构应该大致如此:

项目目录结构

其中 WEB-INF 那个目录如果没有自动创建出来也没关系.

4.1.2 创建 Servlet

现在, 创建一个 Servlet 用于响应前端的请求.

(1) 在 src 文件夹上点右键 → New → Package, 输入”servlets”, 创建一个包 ( Package )

创建包

作为一个 “成熟” 的程序员, 不要把 Java 代码直接放在 src 目录下, 所以我们在 src 下创建一个包(子文件夹)

包名叫什么不重要, 这里取名叫 servlets

(2) 在新建好的 servlets 包的图标上点右键 → New → Java Class, 输入 “SumServlet”, 创建一个 Java 源文件.

搞好后, 大概应该是这样子滴~

创建好Servlet后的目录结构

(3) 打开 SumServlet.java 文件 ( 可能已经自动打开了 ), 输入如下代码并保存.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package servlets;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

// 监听前端请求
@WebServlet(urlPatterns = "/sum.action")
public class SumServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 接收前端传来的参数, 分别存入变量 pa, pb
String pa = req.getParameter("a");
String pb = req.getParameter("b");

// 将接收到参数转换为 Double 型, 以便计算
Double a = Double.parseDouble(pa);
Double b = Double.parseDouble(pb);

// 计算 a + b
Double result = a + b;

// 向前端回应计算结果
PrintWriter out = resp.getWriter();
out.println(result);
}
}

解释一下吧~

上述代码创建一个名为 SumServlet 的 Servlet 用于与前端交互.

参看代码中的注释, 应能大概看懂其逻辑. 下面补充说明几点:

第 11 行, 使用 @WebServlet 注解 ( Annotation ) 声明 SumServlet 类为一个 Servlet, 即可以响应前端请求的入口. 其中的 urlPatterns = "/sum.action" 指明它所监听的前端请求的路径.

注意: 这里的 urlPatterns 的值要与 sum-1.html 第 8 行 / sum-2.html 第 8 行 / sum-3.js 第 3 行对应 !!!

第 12 行, 注意 SumServlet 类要继承 HttpServlet

第 14 ~ 29 行的 service 方法在接收到前端请求时会自动被执行, 形参表中的 req 参数带来了前端传来的信息, 服务器端要回传给前端的信息通过 resp 处理.

温馨提示: 如果你改动到了接口部分, 比如: urlPatterns, 而你的 Tomcat 已经处于运行状态, 那么你应该重新启动一下 Tomcat 才能看到修改后的效果.

4.1.3 组装前端代码

如果你跟着做完了第 3 节的内容, 你应该有一些 html / js 文件, 现在把它们都拷贝到项目的 web 文件夹下, 如下图:

完整的项目结构

闲聊两句~

一般而言, 如果你要做的是一个包含了前端程序和后端程序的完整 Web 项目, 那么你的前端代码应该要和后端代码在同一个 Web 项目下 ( 所以本节要求你把前端代码拷贝到项目的 web 文件夹下 ).

同时, 打开页面的方式不再是直接双击 html 文件图标, 而应该是在浏览器的地址栏中输入正确的访问路径 (见下节).

对于 微信小程序 / Hybrid App , 前端代码已经被打包在小程序 / App 里了, 也就是说, 前端程序和后端程序不可能是在同一个 Web 项目下. 此时更应该注意, 小程序 / App 所指向的服务器端访问地址应该是完整的URL, 即: 形如 http://xxxxx/xxxx 的样子 ( 带协议头 http / https ).

同时, 因为微信小程序 / Hybrid App 与服务器端程序所在的 “域” 不同了, 所以从微信小程序 / Hybrid App 中向服务器端发送 POST 请求时还可能会受到 跨域资源共享 ( CORS ) 的限制, 导致请求失败.

当然, 这不并是无解的问题, 只是出于安全性考虑, HTTP协议设置了这样的限制, 我们可以采用别的机制实现. ( 本文篇幅有限, 就不说了… 如果有必要, 请当面 @ 我, 我再写一篇博文专门讨论一下这个问题 )

本文为求简单, 所有3种方式的前端请求都使用 GET 方式发起. 因此, 不存在 CORS 问题, 放心…

4.1.4 Run / Debug

终于我们可以把程序跑起来看看效果了. 看到 IntelliJ IDEA 窗口右上角的那几个按钮了吗? ( 如下图 )

运行

绿色三角以 运行模式 启动, 绿色虫虫以 调试模式 启动. 选一个你喜欢的, 戳它~

开发阶段最好以 “调试模式” 运行, 这样可以设断点来调试程序.

控制台哗啦哗啦出现一堆消息, 最后停下不动时就表示网站服务器已经启动完成了.

当然, 如果运气不好, 出现错误也是有可能滴~ 最好还是简单瞅一眼控制台的输出, 有没有什么异常 ( Exception )

**打开浏览器, 在地址栏输入 http://localhost:8080/sum-1.html **

应该可以看到 3.1 节做的那个页面, 如图:

sum-1运行效果

点击超链接, 将会跳转至一个新的页面, 其中显示了 3 + 5 的结果: 8.0

3.2, 3.3 节中完成的另外两个页面也自己试试吧~ 浏览器地址栏中的地址把 sum-1.html 部分换成 sum-2.html / sum-3.html 即可.

3.3 节的示例因使用的是 Ajax 方式通信, 并不会导致页面跳转, 而是在当前页面弹出消息框, 显示计算结果.

如果你使用的是 Eclipse, 因为 Eclipse 的默认部署方式是带项目名称的, 所以访问路径还应加上项目名称, 如: http://localhost:8080/example426/sum-1.html.

好了, 但愿你一切顺利~ 阿门!

暂时想不到大家会掉到什么样的坑里, 如果进去了, 自己想想办法, 问问度娘, 实在不行, 给我写信 或 面聊.

最后, 附上完整的项目源代码, 还是那句话, 代码要一行行自己敲才有感觉!

4.2 使用 Node.js 实现

关于 Node.js 开发 Web 应用程序的环境搭建, 请参看 使用 Node.js 开发 Web 应用程序

4.2.1 创建项目

在你喜欢的地方创建一个文件夹 ( 项目文件夹 ), 此处我们取名为 “example426”.

以下是 Linux / CentOS / Mac OS … 操作系统下的控制台命令.

如果你使用 Windows 操作系统, 可以直接在 “资源管理器” / “ 我的电脑” 的地址栏输入 cmd , 回车, 即可在当前文件夹位置打开控制台窗口, 当然也可以按键盘上的 Win + R, 然后输入cmd 打开控制台窗口.

win 键在哪里? 自己百度一下吧~

1
2
3
4
5
$ mkdir example426
$ cd example426
$ npm init
....
$ npm install express --save

上述代码中的 $ 是命令提示符, 是不用手动输入的!

如果在 Windows 操作系统下, $ 的位置大概应该显示的是 C:\> 之类的样子.

好了, 这么简单的一件事情, 罗嗦这么半天… 就怕宝宝们看不懂, 我太难啦~

算了, 还是上个图吧:

命令窗口

好了, 终于可以解释一下上面那段命令的含意了…

第 1 行, 新建一个名叫 example426 的文件夹

第 2 行, 进入 example426 文件夹

第 3 行, 初始化项目. 在执行命令的过程中会问一些问题, 简单起见, 不解释了, 一路回车就行. 如下图:

注意图中红框所示位置, 它表示你程序的入口是项目根目录下的 index.js 这个文件.

创建项目命令

第 3 行的命令执行完后, 执行第 5 行的命令, 在当前项目下安装 express, 并保存配置.

express 是 Node.js 世界常用的 Web 应用程序开发框架.

执行第 5 行命令过程中会从 Internet 下载一些资源, 所以要接入Internet. 同时, 因为资源在国外, 所以会比较慢. 当然, 你可以配置 npm 使用国内镜像站点, 这样就可以飞起来了…

完成后在 example426 文件夹下应该会生成一个文件 package.json 的文件, 它是项目的配置文件, 暂时不用管它.

也许, 还会同时生成一个 package-lock.json 文件, 它是用来 “锁住” 项目依赖包的版本的.

同时, 还会生成一个 node_modules 文件夹, 里面装了项目依赖的东东.

上面三行话如果不明白, 当作没看见就行~

4.2.2 拷入前端代码

在 example426 文件夹下新建一个名为 public 的文件夹, 将前端文件 ( sum-1.html, sum-2.html, sum-3.html, sum-3.js ) 复制到 public 文件夹中.

4.2.3 创建入口文件

在 example426 文件夹下新建一个名为 index.js 的文件, 注意文件名要与执行 npm init 过程中的 entry point 一致. ( 如果你是像我一样, 一路回车, 那文件名就叫 index.js 即可 )

用你喜欢的文本编辑工具或开发工具 ( Windows 自带的记事本都行 ) 打开 index.js 文件, 敲入如入代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const express = require('express');    // 引入 express 框架
const app = express(); // 初始化 express
const port = 8088; // 服务器端监听的端口, 为避免和4.1节项目冲突, 使用8088端口

// 告诉express, 前端静态文件在 public 文件夹下. 应与4.2.2节中新建的文件夹名称一致
app.use(express.static('public'));

// 监听前端请求
app.get('/sum.action', function(req, resp) {
// 接收前端传来的参数, 分别存入 pa, pb
const pa = req.query.a;
const pb = req.query.b;

// 将接收到参数转换为 Float 型, 以便计算
const a = parseFloat(pa);
const b = parseFloat(pb);

// 计算 a + b
const result = a + b;

// 向前端回应计算结果
resp.send({result});
resp.end();
});

// 启动程序
app.listen(port, function() {
console.log('启动成功! 快试试吧~ http://localhost:' + port + '/sum-1.html');
});

套路! 全都是套路!

是不是发现上面这段代码和 4.1.2 节 SumServlet 中的代码长得很像? 自己看注释研究一下吧~

特别注意:

第 9 行, app.get(...) 监听的是前端 GET 请求, 如果前端发来的是 POST 方式的请求, 应改成 app.post(...)

第 11, 12 行, 取得 GET 请求中的参数使用 req.query取得, 后面的 a / b 是参数名. 如果是 POST 的请求, 应换作 req.body

上个图, 到目前为止, example426 文件夹下的结构应该是这样滴:

node-文件结构

4.2.4 Run / Debug

来吧~ 让程序跑起来!

回到控制台窗口, 在 example426 位置执行如下命令:

1
$ node index.js

如果一切正常, 应该看到这个…

node-启动

**打开浏览器, 在地址栏输入 http://localhost:8088/sum-1.html **

应该可以看到 3.1 节做的那个页面, 点击超链接, 将会跳转至一个新的页面, 其中显示了 3 + 5 的结果: {“result”:8}

赶快试试另外的2个页面吧~

项目启动后, 应保持控制台窗口处于打开状态, 若要停止程序, 按 Ctrl + C 即可. 修改了代码后需要停止程序, 重新启动.

5. 后话

现在大家爽了吧, 赶快自己梳理一下, 整理一下心得.

补充几点:

(1) 本文中多次提到 GETPOST 请求, 这是 HTTP 协议中所规定的两种前端请求方法. 具体有什么不同, 参看: HTTP 方法:GET 对比 POST.

在 J2EE 中 可以统一使用 request.getParameter() 方式获取前端传来的参数.

但在多数的技术中 ( ASP / ASP.NET / PHP / Node.js … ) 前端请求的方式不同, 服务器端接收参数的方式也要随着变化 (参见4.2.3节特别注意部分). 否则会报 404 错误.

本文为求简单, 前端使用的都是 GET 方法, 但在实际的项目中, POST 方法使用更多.

(2) 若传输的数据结构复杂, 最好不要使用本文中这种 “离散” 的方式来传递, 如: a=3&b=5 …

应该使用”有格式”的结构化字符串来 “封装” 前后端交互的数据, 这样便于前后端处理.

例如, 可以使用 JSON 格式来封装上面的数据: { “a”: 3, “b”: 5 }

这时, 前端代码 sum-3.js 修改一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function doSum () {
$.ajax({
url : '/sum.action', // 发送数据的目的地
dataType : 'json', // 服务器端返回的数据格式(要与服务器端约定好)
// 向服务器端传送的数据
// 注意: 这里其实是把多个参数组合成一个对象, 然后序列化成 JSON 字符串来传递
data : { params: JSON.stringify({ a: 3, b: 5 }) },
beforeSend : function() {
alert('我要准备发送数据到服务器端了哦~');
},
success : function(data) {
alert(data.result);
}
});
}

注意 第 4 行, dataType 值改为 json, 告诉 jQuery 服务器端回传的数据是 JSON 格式的, 这样 jQuery 会自动将服务器端返回的数据按 JSON 格式进行解析, 并注入第 9 行 success 回调函数的 data 参数中. 也就是说, data 不再是普通的字符串, 而是一个 JavaScript 对象.

第 5 行, 使用 JSON.stringify () 将发往服务器端的数据使用 JSON 格式封装 ( 序列化为 JSON 格式 ).

与此同时, 服务器端的代码也要改一下, 以 4.2.3 节中的代码为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const express = require('express');			// 引入 express 框架
const app = express(); // 初始化 express
const port = 8088; // 服务器端监听的端口, 为避免和4.1节项目冲突, 使用8088端口

// 选择express, 前端静态文件在 public 文件夹下. 应与4.2.2节中新建的文件夹名称一致
app.use(express.static('public'));

// 监听前端请求
app.get('/sum.action', function(req, resp) {

// 接收前端传来的参数, 分别存入 pa, pb
// 前端是把 a, b 两个参数组合成了一个对象, 然后序列化为 JSON 格式字符串上传的
// 对照上面的前端代码中的第 7 行
const params = JSON.parse(req.query.params);
const pa = params.a;
const pb = params.b;

// 将接收到参数转换为 Float 型, 以便计算
const a = parseFloat(pa);
const b = parseFloat(pb);

// 计算 a + b
const result = a + b;

// 向前端回应计算结果
resp.send({result: result}); // 可简写为 resp.send({result});
resp.end();
});

// 启动程序
app.listen(port, function() {
console.log('启动成功! 快试试吧~ http://localhost:' + port + '/sum-1.html');
});

在 4.2.3 节代码基础上修改了 12 ~ 14 行.

注意第 12 行, 因为前端传来的数据被封装成了 JSON 格式字符串, 使用了 params 这个名字, 所以此处从 req.query.params 中取得前端传来的参数, 但因为是 JSON 格式字符串, 所以还使用 JSON.parse() 进行了解析. ( 请对照本节中 sum-3.js 第 5 行仔细研究一下 )

第 24 行, 回传给前端的数据是一个对象: { result: result } , 它会被 express 自动序列化为 JSON 格式.

如果你使用 J2EE 实现服务器端程序, 可以引入第 3 方工具包 ( 比如: gson, fastjson ) 来进行 JSON 的序列化和反序列化.

人困马乏, 天都亮了, 就暂不举例了, 自行百度一下~ 挖个坑, 改天有空再补一个例子.

传送门: 文件上传怎么做