65.9K
CodeProject 正在变化。 阅读更多。
Home

MEAN Stack

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (8投票s)

2018年2月21日

CPOL

3分钟阅读

viewsIcon

13008

downloadIcon

230

MongoDB, Express, AngularJS, Node.js 缩写为 MEAN - 这些开发工具都使用 Javascript。在这个示例中,我们将使用 MSSQL Server 代替 MongoDb。

通过这个应用程序示例,我们可以创建一个新用户,显示所有用户,修改用户数据,以及通过使用 AngularJS 从前端使用 Express 构建的 API 删除用户。

Components

  • MSSql - SQL 数据库
  • Express - NodeJS 框架
  • Angular - 用于前端的 JavaScript 框架
  • Node.js - JavaScript 执行环境 (服务器)

依赖项

强烈建议您查看之前的文章以了解本文

让我们在 MSSql 服务器中创建一个数据库,用于存储用户数据,该数据库将由我们的应用程序操作。 创建数据库后,通过使用该数据库执行以下查询。

CREATE TABLE [dbo].[User](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Name] [nvarchar](250) NULL,
	[Email] [nvarchar](250) NULL,
	[Phone] [nvarchar](50) NULL,
 CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, _
       IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

这将创建一个名为 user 的新表。 之后,我们需要创建一些存储过程用于 SQL 操作,复制下面的脚本并将其粘贴到 MSSql 查询窗口中执行。

CREATE PROCEDURE [dbo].[GetUsers]
	-- Add the parameters for the stored procedure here
	
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

    -- Insert statements for procedure here
	SELECT * FROM [dbo].[User]
END
GO

CREATE PROCEDURE [dbo].[GetUserById]
	@Id Int
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

    -- Insert statements for procedure here
	SELECT * FROM [dbo].[User] WHERE Id = @Id
END
GO
CREATE PROCEDURE [dbo].[PutUser]
	-- Add the parameters for the stored procedure here
	@Id INT,
	@Name NVarchar(250),
	@Email NVarchar(250),
	@Phone NVarchar(50)
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

    -- Insert statements for procedure here
	Update [dbo].[User] 
	SET [Name] = @Name,[Email] = @Email,[Phone] = @Phone
	WHERE [Id] = @Id
END
GO
CREATE PROCEDURE [dbo].[SetUser]
	-- Add the parameters for the stored procedure here
	@Name NVarchar(250),
	@Email NVarchar(250),
	@Phone NVarchar(50)
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

    -- Insert statements for procedure here
	INSERT INTO [dbo].[User]([Name],[Email],[Phone])
	VALUES(@Name,@Email,@Phone)

END
GO

CREATE PROCEDURE [dbo].[DeleteUser]
	-- Add the parameters for the stored procedure here
	@Id Int
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

    -- Insert statements for procedure here
	DELETE FROM [dbo].[User]
	WHERE [Id] = @Id
END

--Exec DeleteUser 1
GO

我们已经完成了数据库工作。 让我们开始应用程序开发计划。 从我们之前的应用程序示例开始。

GitHub 下载它,然后使用 Visual Studio 2017 打开应用程序。我们需要安装另外两个软件包才能满足我们的要求。

  • mssql - 用于 Node.js 的 Microsoft SQL Server 客户端
  • body-parser - Node.js body 解析中间件

服务器端

包安装

右键单击项目,转到 > 在此处打开命令提示符。 运行此命令以安装 mssql

npm install mssql

运行此命令以安装 body-parser

npm install body-parser

完成此安装后,我们需要将它们添加到我们的节点服务器。 在这里,我们通过启用添加了它们。

var bodyParser = require("body-parser");

然后,我们必须将其用于我们的应用程序。

//Body Parser Middleware
app.use(bodyParser.json()
app.use(bodyParser.urlencoded({ extended: true }));

在此示例中,操作将通过使用 Express 路由的 API 执行。 让我们开始创建 API 的过程。

API

让我们创建数据服务以在数据库中执行操作。 添加一个公共 dbService.js 文件来处理请求,如下图所示

打开新添加的 js 文件,然后添加此行

var mssql = require('mssql');

这意味着我们通过 require() 函数来加载 mssql 模块,从而在新函数中加载源代码。

之后,我们添加数据库服务器连接配置

var dbConfig = {
    user: "sa",
    password: "sa@12345",
    server: "DESKTOP-80DEJMQ",
    database: "dbNode",
    pool: {
        max: 10,
        min: 0,
        idleTimeoutMillis: 30000
    }
};

正如我们所看到的,max 池是 10min0,以及关闭未使用的连接之前的超时时间(以毫秒为单位),这是默认值。

获取有关池的更多详细信息:https://github.com/coopernurse/node-pool

var executeQuery = function (sql, res) {
    const conn = new mssql.ConnectionPool(dbConfig);
    conn.connect().then(function () {
        const req = new mssql.Request(conn);
        req.query(sql).then(function (data) {
            res(data);
        }).catch(function (err) {
            res(null, err);
        })
    }).catch(function (err) {
        res(null, err);
    })
}

连接

const conn = new mssql.ConnectionPool(dbConfig);

我们正在使用 connectionpool 创建 SQL 连接对象。

请求

const req = new mssql.Request(conn);

然后,我们使用全局连接池执行请求。

获取有关连接的更多详细信息:https://npmjs.net.cn/package/mssql#connections-1

最后,我们正在导出 module 以供另一个 module 调用。

module.exports = {
    executeQuery
}

让我们使用 Express 路由器创建一个特定的数据服务。 创建一个新的 JS 文件,然后将下面的代码片段添加到新添加的文件中。

var express = require('express');
var router = express.Router();
var dbService = require('../dbService');

//GET API
router.get("/api/user/getAll", function (req, res) {
    var query = "GetUsers";
    dbService.executeQuery(query, function (data, err) {
        if (err) {
            throw err;
        } else {
            res.send(data.recordset);
        }
        res.end();
    });
});

// GET API
router.get("/api/user/getUser/:id", function (req, res) {
    var query = "[GetUserById] " + parseInt(req.params.id) + "";

    dbService.executeQuery(query, function (data, err) {
        if (err) {
            throw err;
        } else {
            res.send(data.recordset);
        }
        res.end();
    });
});

//POST API
router.post("/api/user/setUser", function (req, res) {
    var query = "[SetUser] '" + req.body.Name + "', '" + 
                  req.body.Email + "', '" + req.body.Phone + "'";
    dbService.executeQuery(query, function (data, err) {
        if (err) {
            throw err;
        } else {
            res.send(data.recordset);
        }
        res.end();
    });
});

//PUT API
router.put("/api/user/putUser", function (req, res) {
    var query = "[PutUser] " + parseInt(req.body.Id) + ", '" + 
    req.body.Name + "','" + req.body.Email + "', '" + req.body.Phone + "'";
    dbService.executeQuery(query, function (data, err) {
        if (err) {
            throw err;
        } else {
            res.send(data.recordset);
        }
        res.end();
    });
});

//DELETE API
router.delete("/api/user/deleteUser/:id", function (req, res) {
    var query = "[DeleteUser] " + parseInt(req.params.id) + "";

    dbService.executeQuery(query, function (data, err) {
        if (err) {
            throw err;
        } else {
            res.send(data.recordset);
        }
        res.end();
    });
});

module.exports = router;

完成所有这些操作后,我们需要通过 node 服务器中的 app.use() 函数将其作为中间件启用。

//Router Middleware
app.use('/', require('./data/userService/userDataService'));

此外,我们通过启用跨域资源共享 (CORS) 来授予其他用户代理访问权限。

//CORS Middleware
app.use(function (req, res, next) {
    //Enabling CORS 
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
    res.header("Access-Control-Allow-Headers", 
    "Origin, X-Requested-With, contentType,Content-Type, Accept, Authorization");
    next();
}); 

最后,这是 Node 服务器的概述

'use strict';
//var http = require('http');
var bodyParser = require("body-parser");
var path = require('path');
var express = require('express');
var app = express();
var port = process.env.port || 3000;

//Body Parser Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

//Serve Static Files
app.use(express.static(path.join(__dirname, 'app')));
app.use(express.static(path.join(__dirname, 'public')));

//Router Middleware
app.use('/', require('./data/userService/userDataService'));

//CORS Middleware
app.use(function (req, res, next) {
    //Enabling CORS 
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
    res.header("Access-Control-Allow-Headers", "Origin, 
    X-Requested-With, contentType,Content-Type, Accept, Authorization");
    next();
});

app.get('/*', function (req, res) {
    res.sendFile(path.resolve('layout.html'));
});

app.get('/*', function (req, res) {
    res.render('error');
});

var server = app.listen(port, function () {
    console.log('Node server is running on port..' + port);
});

客户端

我们需要创建用户界面来操作数据库中的数据。 首先,我们需要为特定模块创建文件夹,如下所示

如您所见,我们创建了一个带有 HTML 页面的 AngularJS 控制器。

Html 视图

当路由状态更改时,将呈现此视图。
<div class="container-fluid">
    <div class="row">
        <div class="col-sm-4">
            <h3>Add New User</h3>

            <form name="frmUser" novalidate>
                <input type="hidden" ng-model="userModel.Id" name="uid" />

                <div class="form-group">
                    <label for="uname" class="control-label">User Name</label>
                    <input type="text" class="form-control" 
                     ng-model="userModel.Name" name="uname" placeholder="" required />
                    <span class="error" ng-show="(frmUser.$dirty||submitted) && 
                     frmUser.uname.$error.required">Customer name is Required</span>

                </div>
                <div class="form-group">
                    <label for="email" class="control-label">E-mail</label>
                    <input type="email" class="form-control" 
                     ng-model="userModel.Email" name="email" 
                     placeholder="" required />
                    <span class="error" ng-show="(frmUser.$dirty ||submitted) && 
                     frmUser.email.$error.required">EmailId is Required!</span>
                    <span class="error" ng-show="(frmUser.$dirty ||submitted) && 
                     frmUser.$error.email">Invalid EmailId!</span>

                </div>
                <div class="form-group">
                    <label for="phone" class="control-label">Phone</label>
                    <input type="text" class="form-control" 
                     ng-model="userModel.Phone" name="phone" placeholder="" />

                </div>
                <div class="form-group">
                    <button type="submit" class="btn btn-danger" 
                     ng-click="reset()">Reset</button>
                    <button type="submit" class="btn btn-primary" 
                     ng-click="saveUser()" ng-disabled="frmUser.$invalid"
                            ng-If="userModel.Id == 0">
                        Create
                    </button>
                    <button type="submit" class="btn btn-success" 
                     ng-click="updateUser()" ng-disabled="frmUser.$invalid"
                            ng-If="userModel.Id > 0">
                        Update
                    </button>
                </div>
            </form>
            <span class="warning">{{resmessage}}</span>
        </div>
        <div class="col-sm-8">
            <h3>All User</h3>
            <table style="width:100%" class="table table-striped">
                <tr>
                    <th>Sr.</th>
                    <th>Name</th>
                    <th>Email</th>
                    <th>Phone</th>
                    <th>Option</th>
                </tr>
                <tr ng-repeat="item in ListUser">
                    <td>{{ $index+1 }}</td>
                    <td>{{ item.Name }}</td>
                    <td>{{ item.Email }}</td>
                    <td>{{ item.Phone }}</td>
                    <td>
                        <a href="#" ng-click="getUser(item)" 
                         title="Edit Record" class="btn btn-primary btn-xs pull-right">
                            Edit
                        </a>
                        <a href="#" ng-click="deleteUser(item)" 
                         title="Delete Record" class="btn btn-danger btn-xs pull-right">
                            Delete
                        </a>
                    </td>
                </tr>
            </table>
        </div>
    </div>
</div>

AngularJS 控制器

在我们的 AngularJS 控制器中,我们必须使用 $http 服务与 API 通信。 使用的方法

  • $http.get:获取数据
  • $http.post:发布新数据
  • $http.put:更新现有数据
  • $http.delete:删除现有数据

有关 $http 服务 的更多信息,请参见此处。

templatingApp.controller('UserController', 
    ['$scope', '$http', function ($scope, $http) {
    $scope.title = "All User";
    $scope.ListUser = null;
    $scope.userModel = {};
    $scope.userModel.Id = 0;
    getallData();

    //******=========Get All User=========******
    function getallData() {
        $http({
            method: 'GET',
            url: '/api/user/getAll/'
        }).then(function (response) {
            $scope.ListUser = response.data;
        }, function (error) {
            console.log(error);
        });
    };

    //******=========Get Single User=========******
    $scope.getUser = function (user) {
        $http({
            method: 'GET',
            url: '/api/user/getUser/' + parseInt(user.Id)
        }).then(function (response) {
            $scope.userModel = response.data[0];
        }, function (error) {
            console.log(error);
        });
    };

    //******=========Save User=========******
    $scope.saveUser = function () {
        $http({
            method: 'POST',
            url: '/api/user/setUser/',
            data: $scope.userModel
        }).then(function (response) {
            showNotif("Data Saved")
            $scope.reset();
            getallData();
        }, function (error) {
            console.log(error);
        });
    };

    //******=========Update User=========******
    $scope.updateUser = function () {
        $http({
            method: 'PUT',
            url: '/api/user/putUser/',
            data: $scope.userModel
        }).then(function (response) {
            showNotif("Data Updated")
            $scope.reset();
            getallData();
        }, function (error) {
            console.log(error);
        });
    };

    //******=========Delete User=========******
    $scope.deleteUser = function (user) {
        var IsConf = confirm('You are about to delete ' + 
                              user.Name + '. Are you sure?');
        if (IsConf) {
            $http({
                method: 'DELETE',
                url: '/api/user/deleteUser/' + parseInt(user.Id)
            }).then(function (response) {
                showNotif("Data Deleted")
                $scope.reset();
                getallData();
            }, function (error) {
                console.log(error);
            });
        }
    };

    //******=========Clear Form=========******
    $scope.reset = function () {
        var msg = "Form Cleared";
        $scope.userModel = {};
        $scope.userModel.Id = 0;
        showNotif(msg)
    };
}]);

发布应用程序

让我们转到 gulp 修改以最终获取 publish 文件。

    gulp.task('publish', function () {
    gulp.src('layout.html')
        .pipe(gulp.dest(paths.publish));
    gulp.src('package.json')
        .pipe(gulp.dest(paths.publish));
    gulp.src('server.js')
        .pipe(gulp.dest(paths.publish));

    gulp.src('app/**/*')
        .pipe(gulp.dest(paths.publish + 'app'));
    gulp.src('public/**/*')
        .pipe(gulp.dest(paths.publish + 'public'));
    gulp.src('data/**/*')
        .pipe(gulp.dest(paths.publish + 'data'));
    gulp.src('bin/**/*')
        .pipe(gulp.dest(paths.publish + 'bin'));
});

转到 Visual Studio 中的任务资源管理器,如下图所示

运行该任务,这会将我们所有的应用程序文件复制到已发布的文件夹。

转到 Publish 文件夹

在此处打开命令提示符(Shift + 鼠标右键),然后键入 nodemon。 我们正在使用 nodemon 启动我们的应用程序。 如果我们的应用程序有任何更改,nodemon 将自动重启该应用程序。

现在打开浏览器,键入 URL:https://:3000。

输出

希望这能有所帮助。:)

历史

  • 2018 年 2 月 21 日:初始版本
© . All rights reserved.