Contents

使用AngularJS和Spring Data REST规范创建 CRUD 程序

1. 概述

在本教程中,我们将创建一个简单的 CRUD 应用程序示例,使用 AngularJS 作为前端,使用 Spring Data REST 作为后端。

2. 创建 REST 数据服务

为了创建对持久性的支持,我们将使用 Spring Data REST 规范,该规范将使我们能够对数据模型执行 CRUD 操作。

您可以在Spring Data REST 简介 中找到有关如何设置 REST 端点的所有必要信息。在本文中,我们将重用我们为介绍教程设置的现有项目。

对于持久性,我们将使用内存数据库中的H2

作为数据模型,上一篇文章定义了一个WebsiteUser类,具有idnameemail属性以及一个名为UserRepository的存储库接口。

定义此接口指示 Spring 创建对公开 REST 集合资源和项目资源的支持。现在让我们仔细看看我们稍后将从AngularJS调用的端点。

2.1. Collection 资源

我们将在端点*/users*处获得所有用户的列表。可以使用 GET 方法调用此 URL,并将返回以下形式的 JSON 对象:

{
  "_embedded" : {
    "users" : [ {
      "name" : "Ann",
      "age" : 20,
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/users/1"
        },
        "User" : {
          "href" : "http://localhost:8080/users/1"
        }
      }
    }, 
...
    ]
  }
}

2.2. Item 资源

可以通过使用不同的 HTTP 方法和请求有效负载访问*/users/{userID}形式的 URL 来操作单个WebsiteUser*对象。

为了检索WebsiteUser对象,我们可以使用GET 方法访问*/users/{userID}* 。这将返回以下形式的 JSON 对象:

{
  "name" : "Ann",
  "email" : "ann@yahoo.com",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/users/1"
    },
    "User" : {
      "href" : "http://localhost:8080/users/1"
    }
  }
}

要添加新的WebsiteUser,我们需要使用 POST 方法调用*/users* 。新的WebsiteUser记录的属性将作为 JSON 对象添加到请求正文中:

{name: "Ann", email: "ann@yahoo.com"}

如果没有错误,此 URL 将返回状态代码 201 CREATED。

如果我们想更新WebsiteUser记录的属性,我们需要使用 PATCH 方法和包含新值的请求正文调用 URL /users/{UserID}

{name: "Ann", email: "ann@yahoo.com"}

要删除WebsiteUser记录,我们可以使用 DELETE 方法调用 URL /users/{UserID} 。如果没有错误,则返回状态码 204 NO CONTENT。

2.3. MVC 配置

我们还将添加一个基本的 MVC 配置以在我们的应用程序中显示 html 文件:

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
    
    public MvcConfig(){
        super();
    }
    
    @Override
    public void configureDefaultServletHandling(
      DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
   @Bean
   WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> enableDefaultServlet() {
     return (factory) -> factory.setRegisterDefaultServlet(true);
   }
}

2.4. 允许跨域请求

如果我们想将AngularJS前端应用程序与 REST API 分开部署——那么我们需要启用跨域请求。

Spring Data REST 从版本 1.5.0.RELEASE 开始添加了对此的支持。要允许来自不同域的请求,您只需将*@CrossOrigin*注释添加到存储库:

@CrossOrigin
@RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserRepository extends CrudRepository<WebsiteUser, Long> {}

因此,在来自 REST 端点的每个响应中,都会添加一个Access-Control-Allow-Origin标头。

3. 创建 AngularJS 客户端

为了创建我们的 CRUD 应用程序的前端,我们将使用AngularJS——一个众所周知的 JavaScript 框架,它简化了前端应用程序的创建。

为了使用AngularJS,我们首先需要在我们的 html 页面中包含angular.min.js文件,该文件将被称为users.html

<script 
  src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js">
</script>

接下来,我们需要创建一个 Angular 模块、控制器和服务,它们将调用 REST 端点并显示返回的数据。

这些将被放置在一个名为app.js的 JavaScript 文件中,该文件也需要包含在users.html页面中:

<script src="view/app.js"></script>

3.1. Angular 服务

首先,让我们创建一个名为UserCRUDService的 Angular 服务,它将利用注入的AngularJS $http服务来调用服务器。每个调用都将放在一个单独的方法中。

让我们看一下使用*/users/{userID}*端点定义通过 id 检索用户的方法:

app.service('UserCRUDService', [ '$http', function($http) {
    this.getUser = function getUser(userId) {
        return $http({
            method : 'GET',
            url : 'users/' + userId
        });
    }
} ]);

接下来,让我们定义addUser方法,该方法向*/users* URL 发出POST 请求,并在data属性中发送用户值:

this.addUser = function addUser(name, email) {
    return $http({
        method : 'POST',
        url : 'users',
        data : {
            name : name,
            email: email
        }
    });
}

updateUser方法与上面的方法类似,不同之处在于它将有一个id参数并发出 PATCH 请求:

this.updateUser = function updateUser(id, name, email) {
    return $http({
        method : 'PATCH',
        url : 'users/' + id,
        data : {
            name : name,
            email: email
        }
    });
}

删除WebsiteUser记录的方法会发出 DELETE 请求:

this.deleteUser = function deleteUser(id) {
    return $http({
        method : 'DELETE',
        url : 'users/' + id
    })
}

最后,让我们看一下检索整个用户列表的方法:

this.getAllUsers = function getAllUsers() {
    return $http({
        method : 'GET',
        url : 'users'
    });
}

所有这些服务方法都将由AngularJS控制器调用。

3.2. AngularJS 控制器

我们将创建一个UserCRUDCtrl AngularJS控制器,该控制器将注入一个UserCRUDService并使用服务方法从服务器获取响应,处理successerror情况,并设置包含响应数据的*$scope*变量以在 HTML 页面中显示它。

我们看一下调用getUser(userId)服务函数的getUser()函数,定义了成功和错误的两个回调方法。如果服务器请求成功,则将响应保存在user变量中;否则,将处理错误消息:

app.controller('UserCRUDCtrl', ['$scope','UserCRUDService', 
  function ($scope,UserCRUDService) {
      $scope.getUser = function () {
          var id = $scope.user.id;
          UserCRUDService.getUser($scope.user.id)
            .then(function success(response) {
                $scope.user = response.data;
                $scope.user.id = id;
                $scope.message='';
                $scope.errorMessage = '';
            },
    	    function error (response) {
                $scope.message = '';
                if (response.status === 404){
                    $scope.errorMessage = 'User not found!';
                }
                else {
                    $scope.errorMessage = "Error getting user!";
                }
            });
      };
}]);

*addUser()*函数将调用相应的服务函数并处理响应:

$scope.addUser = function () {
    if ($scope.user != null && $scope.user.name) {
        UserCRUDService.addUser($scope.user.name, $scope.user.email)
          .then (function success(response){
              $scope.message = 'User added!';
              $scope.errorMessage = '';
          },
          function error(response){
              $scope.errorMessage = 'Error adding user!';
              $scope.message = '';
        });
    }
    else {
        $scope.errorMessage = 'Please enter a name!';
        $scope.message = '';
    }
}

*updateUser()deleteUser()*函数与上面的函数类似:

$scope.updateUser = function () {
    UserCRUDService.updateUser($scope.user.id, 
      $scope.user.name, $scope.user.email)
      .then(function success(response) {
          $scope.message = 'User data updated!';
          $scope.errorMessage = '';
      },
      function error(response) {
          $scope.errorMessage = 'Error updating user!';
          $scope.message = '';
      });
}
$scope.deleteUser = function () {
    UserCRUDService.deleteUser($scope.user.id)
      .then (function success(response) {
          $scope.message = 'User deleted!';
          $scope.User = null;
          $scope.errorMessage='';
      },
      function error(response) {
          $scope.errorMessage = 'Error deleting user!';
          $scope.message='';
      });
}

最后,让我们定义检索用户列表并将其存储在users变量中的函数:

$scope.getAllUsers = function () {
    UserCRUDService.getAllUsers()
      .then(function success(response) {
          $scope.users = response.data._embedded.users;
          $scope.message='';
          $scope.errorMessage = '';
      },
      function error (response) {
          $scope.message='';
          $scope.errorMessage = 'Error getting users!';
      });
}

3.3. 网页

users.html页面将使用上一节中定义的控制器函数和存储的变量。

首先,为了使用 Angular 模块,我们需要设置ng-app属性:

<html ng-app="app">

然后,为了避免每次使用控制器的函数时都输入UserCRUDCtrl.getUser(),我们可以将 HTML 元素包装在一个带有ng-controller属性集的div中:

<div ng-controller="UserCRUDCtrl">

让我们创建一个表单来输入和显示我们想要操作的WebiteUser对象的值。其中每一个都有一个ng-model属性集,它将它绑定到属性的值:

<table>
    <tr>
        <td width="100">ID:</td>
        <td><input type="text" id="id" ng-model="user.id" /></td>
    </tr>
    <tr>
        <td width="100">Name:</td>
        <td><input type="text" id="name" ng-model="user.name" /></td>
    </tr>
    <tr>
        <td width="100">Age:</td>
        <td><input type="text" id="age" ng-model="user.email" /></td>
    </tr>
</table>

例如,将id输入绑定到user.id变量意味着每当输入的值发生更改时,都会在user.id变量中设置该值,反之亦然。

接下来,让我们使用ng-click属性来定义将触发调用定义的每个 CRUD 控制器函数的链接:

<a ng-click="getUser(user.id)">Get User</a>
<a ng-click="updateUser(user.id,user.name,user.email)">Update User</a>
<a ng-click="addUser(user.name,user.email)">Add User</a>
<a ng-click="deleteUser(user.id)">Delete User</a>

最后,让我们按名称完整显示用户列表:

<a ng-click="getAllUsers()">Get all Users</a><br/><br/>
<div ng-repeat="usr in users">
{{usr.name}} {{usr.email}}