Spring4 MVC+ AngularJS CRUD使用$http實例
這篇文章顯示了使用Spring MVC4整合AngularJS。 我們將創建一個使用後端和AngularJS作爲前端的純JSP封裝Spring REST API一個CRUD應用程序, 使用 $http 服務與服務器異步通信。我們還將進行使用UI AngularJS表單驗證各種驗證。
在我們的應用程序,客戶端是基於AngularJS和服務器端是基於Spring REST API。
只是爲了學習的話,你可以在這裏本例中使用的模板。 它是一個小型應用程序,這是不符合服務器交互。所以需要刷新頁面重新加載數據。
但是不要擔心。完整應用程序將開發會在這篇文章後,進一步詳細開發和講解。
現在讓我們開始!
使用以下技術:
- Spring 4.2.0.RELEASE
- AngularJS 1.4.4
- Maven 3
- JDK 1.7
- Eclipse JUNO Service Release 2
- M2Eclipse plugin (Optional)
工程目錄結構
提供相關依賴關係
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4\_0\_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yiibai.springmvc</groupId>
<artifactId>Spring4MVCAngularJSExample</artifactId>
<packaging>war</packaging>
<version>1.0.0</version>
<name>Spring4MVCAngularJSExample Maven Webapp</name>
<properties>
<springframework.version>4.2.0.RELEASE</springframework.version>
<jackson.version>2.5.3</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<warSourceDirectory>src/main/webapp</warSourceDirectory>
<warName>Spring4MVCAngularJSExample</warName>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<finalName>Spring4MVCAngularJSExample</finalName>
</build>
</project>
1.客戶端(前端)
這裏的應用程序的客戶端是基於AngularJS。如果你想學習AngularJs,那麼不訪看看我們的 AngularJS教程 可讓你在最流行的JavaScript框架有一個瞭解認識。
創建AngularJS模塊
模塊是AngularJS應用中最重要的部分。AngularJS模塊你可以認爲是在Java中的包。它應用程序的容器有不同部分 - 控制器,服務器,過濾器,指令等。AngularJS可以組合某些功能/Javascript在一起在單個模塊之下。
模塊可用於通過AngularJS來引導應用程序。 通過傳遞模塊名到 ng-app 指令,我們可以告訴AngularJS加載該模塊的應用程序主入口點。
app.js
'use strict';
var App = angular.module('myApp',\[\]);
創建AngularJS服務和服務器進行通信
在我們的應用中,將與Spring REST API,例如基於後端服務器的方式進行通信。在基於AngularJS應用中,與服務器進行通信的首選方式是使用AngularJS內置的 $http 服務。 AngularJS$ http服務允許我們使用XHR[瀏覽器的XMLHttpRequest對象] API與服務器端點通信。
user_service.js
'use strict';
App.factory('UserService', \['$http', '$q', function($http, $q){
return {
fetchAllUsers: function() {
return $http.get('http://localhost:8080/Spring4MVCAngularJSExample/user/')
.then(
function(response){
return response.data;
},
function(errResponse){
console.error('Error while fetching users');
return $q.reject(errResponse);
}
);
},
createUser: function(user){
return $http.post('http://localhost:8080/Spring4MVCAngularJSExample/user/', user)
.then(
function(response){
return response.data;
},
function(errResponse){
console.error('Error while creating user');
return $q.reject(errResponse);
}
);
},
updateUser: function(user, id){
return $http.put('http://localhost:8080/Spring4MVCAngularJSExample/user/'+id, user)
.then(
function(response){
return response.data;
},
function(errResponse){
console.error('Error while updating user');
return $q.reject(errResponse);
}
);
},
deleteUser: function(id){
return $http.delete('http://localhost:8080/Spring4MVCAngularJSExample/user/'+id)
.then(
function(response){
return response.data;
},
function(errResponse){
console.error('Error while deleting user');
return $q.reject(errResponse);
}
);
}
};
}\]);
創建AngularJS控制器
控制器是一個AngularJS應用的最有用的部分。這些是執行JavaScript函數/對象大多數用戶界面相關的工作。它們可以被看作是驅動程序模型和視圖的變化。它們是模型(在我們的應用程序中的數據),和視圖(無論用戶看到在屏幕上以及與其交互)之間的網關。
user_controller.js
'use strict';
App.controller('UserController', \['$scope', 'UserService', function($scope, UserService) {
var self = this;
self.user={id:null,username:'',address:'',email:''};
self.users=\[\];
self.fetchAllUsers = function(){
UserService.fetchAllUsers()
.then(
function(d) {
self.users = d;
},
function(errResponse){
console.error('Error while fetching Currencies');
}
);
};
self.createUser = function(user){
UserService.createUser(user)
.then(
self.fetchAllUsers,
function(errResponse){
console.error('Error while creating User.');
}
);
};
self.updateUser = function(user, id){
UserService.updateUser(user, id)
.then(
self.fetchAllUsers,
function(errResponse){
console.error('Error while updating User.');
}
);
};
self.deleteUser = function(id){
UserService.deleteUser(id)
.then(
self.fetchAllUsers,
function(errResponse){
console.error('Error while deleting User.');
}
);
};
self.fetchAllUsers();
self.submit = function() {
if(self.user.id===null){
console.log('Saving New User', self.user);
self.createUser(self.user);
}else{
self.updateUser(self.user, self.user.id);
console.log('User updated with id ', self.user.id);
}
self.reset();
};
self.edit = function(id){
console.log('id to be edited', id);
for(var i = 0; i < self.users.length; i++){
if(self.users\[i\].id === id) {
self.user = angular.copy(self.users\[i\]);
break;
}
}
};
self.remove = function(id){
console.log('id to be deleted', id);
if(self.user.id === id) {//clean form if the user to be deleted is shown there.
self.reset();
}
self.deleteUser(id);
};
self.reset = function(){
self.user={id:null,username:'',address:'',email:''};
$scope.myForm.$setPristine(); //reset Form
};
}\]);
Spring MVC應用程序的創建視圖
在這裏,我們在操作上有些傳統,並用純JSP封裝我們上面寫的所有的AngularJS代碼。請注意,您可以使用其他FE技術,而不是JSP(Velocity模板爲例)。我們還增加了bootstrap 以增強其外觀和感覺。此外,我們也將進行必要的表單驗證。
UserManagement.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>AngularJS $http Example</title>
<style>
.username.ng-valid {
background-color: lightgreen;
}
.username.ng-dirty.ng-invalid-required {
background-color: red;
}
.username.ng-dirty.ng-invalid-minlength {
background-color: yellow;
}
.email.ng-valid {
background-color: lightgreen;
}
.email.ng-dirty.ng-invalid-required {
background-color: red;
}
.email.ng-dirty.ng-invalid-email {
background-color: yellow;
}
</style>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link href="<c:url value='/static/css/app.css' />" rel="stylesheet"></link>
</head>
<body ng-app="myApp" class="ng-cloak">
<div class="generic-container" ng-controller="UserController as ctrl">
<div class="panel panel-default">
<div class="panel-heading"><span class="lead">User Registration Form </span></div>
<div class="formcontainer">
<form ng-submit="ctrl.submit()" name="myForm" class="form-horizontal">
<input type="hidden" ng-model="ctrl.user.id" />
<div class="row">
<div class="form-group col-md-12">
<label class="col-md-2 control-lable" for="uname">Name</label>
<div class="col-md-7">
<input type="text" ng-model="ctrl.user.username" id="uname" class="username form-control input-sm" placeholder="Enter your name" required ng-minlength="3"/>
<div class="has-error" ng-show="myForm.$dirty">
<span ng-show="myForm.uname.$error.required">This is a required field</span>
<span ng-show="myForm.uname.$error.minlength">Minimum length required is 3</span>
<span ng-show="myForm.uname.$invalid">This field is invalid </span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="form-group col-md-12">
<label class="col-md-2 control-lable" for="address">Address</label>
<div class="col-md-7">
<input type="text" ng-model="ctrl.user.address" id="address" class="form-control input-sm" placeholder="Enter your Address. \[This field is validation free\]"/>
</div>
</div>
</div>
<div class="row">
<div class="form-group col-md-12">
<label class="col-md-2 control-lable" for="email">Email</label>
<div class="col-md-7">
<input type="email" ng-model="ctrl.user.email" id="email" class="email form-control input-sm" placeholder="Enter your Email" required/>
<div class="has-error" ng-show="myForm.$dirty">
<span ng-show="myForm.email.$error.required">This is a required field</span>
<span ng-show="myForm.email.$invalid">This field is invalid </span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="form-actions floatRight">
<input type="submit" value="{{!ctrl.user.id ? 'Add' : 'Update'}}" class="btn btn-primary btn-sm" ng-disabled="myForm.$invalid">
<button type="button" ng-click="ctrl.reset()" class="btn btn-warning btn-sm" ng-disabled="myForm.$pristine">Reset Form</button>
</div>
</div>
</form>
</div>
</div>
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading"><span class="lead">List of Users </span></div>
<div class="tablecontainer">
<table class="table table-hover">
<thead>
<tr>
<th>ID.</th>
<th>Name</th>
<th>Address</th>
<th>Email</th>
<th width="20%"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="u in ctrl.users">
<td><span ng-bind="u.id"></span></td>
<td><span ng-bind="u.username"></span></td>
<td><span ng-bind="u.address"></span></td>
<td><span ng-bind="u.email"></span></td>
<td>
<button type="button" ng-click="ctrl.edit(u.id)" class="btn btn-success custom-width">Edit</button> <button type="button" ng-click="ctrl.remove(u.id)" class="btn btn-danger custom-width">Remove</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>
<script src="<c:url value='/static/js/app.js' />"></script>
<script src="<c:url value='/static/js/service/user\_service.js' />"></script>
<script src="<c:url value='/static/js/controller/user\_controller.js' />"></script>
</body>
</html>
2. 服務器端
創建REST控制器的Spring MVC應用程序
下面顯示的是一個基於REST控制器。這相同於 Spring MVC中4 RESTful Web服務的CRUD例子+RestTemplate中的控制器。唯一的區別在於用戶[模型對象]具有不同的特性,這是根據在本實例的用戶界面。
package com.yiibai.springmvc.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import com.yiibai.springmvc.model.User;
import com.yiibai.springmvc.service.UserService;
@RestController
public class HelloWorldRestController {
@Autowired
UserService userService; //Service which will do all data retrieval/manipulation work
//-------------------Retrieve All Users--------------------------------------------------------
@RequestMapping(value = "/user/", method = RequestMethod.GET)
public ResponseEntity<List<User>> listAllUsers() {
List<User> users = userService.findAllUsers();
if(users.isEmpty()){
return new ResponseEntity<List<User>>(HttpStatus.NO\_CONTENT);//You many decide to return HttpStatus.NOT\_FOUND
}
return new ResponseEntity<List<User>>(users, HttpStatus.OK);
}
//-------------------Retrieve Single User--------------------------------------------------------
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION\_JSON\_VALUE)
public ResponseEntity<User> getUser(@PathVariable("id") long id) {
System.out.println("Fetching User with id " + id);
User user = userService.findById(id);
if (user == null) {
System.out.println("User with id " + id + " not found");
return new ResponseEntity<User>(HttpStatus.NOT\_FOUND);
}
return new ResponseEntity<User>(user, HttpStatus.OK);
}
//-------------------Create a User--------------------------------------------------------
@RequestMapping(value = "/user/", method = RequestMethod.POST)
public ResponseEntity<Void> createUser(@RequestBody User user, UriComponentsBuilder ucBuilder) {
System.out.println("Creating User " + user.getUsername());
if (userService.isUserExist(user)) {
System.out.println("A User with name " + user.getUsername() + " already exist");
return new ResponseEntity<Void>(HttpStatus.CONFLICT);
}
userService.saveUser(user);
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ucBuilder.path("/user/{id}").buildAndExpand(user.getId()).toUri());
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
//------------------- Update a User --------------------------------------------------------
@RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
public ResponseEntity<User> updateUser(@PathVariable("id") long id, @RequestBody User user) {
System.out.println("Updating User " + id);
User currentUser = userService.findById(id);
if (currentUser==null) {
System.out.println("User with id " + id + " not found");
return new ResponseEntity<User>(HttpStatus.NOT\_FOUND);
}
currentUser.setUsername(user.getUsername());
currentUser.setAddress(user.getAddress());
currentUser.setEmail(user.getEmail());
userService.updateUser(currentUser);
return new ResponseEntity<User>(currentUser, HttpStatus.OK);
}
//------------------- Delete a User --------------------------------------------------------
@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public ResponseEntity<User> deleteUser(@PathVariable("id") long id) {
System.out.println("Fetching & Deleting User with id " + id);
User user = userService.findById(id);
if (user == null) {
System.out.println("Unable to delete. User with id " + id + " not found");
return new ResponseEntity<User>(HttpStatus.NOT\_FOUND);
}
userService.deleteUserById(id);
return new ResponseEntity<User>(HttpStatus.NO\_CONTENT);
}
//------------------- Delete All Users --------------------------------------------------------
@RequestMapping(value = "/user/", method = RequestMethod.DELETE)
public ResponseEntity<User> deleteAllUsers() {
System.out.println("Deleting All Users");
userService.deleteAllUsers();
return new ResponseEntity<User>(HttpStatus.NO\_CONTENT);
}
}
創建Spring MVC應用程序的主控制器
這這將成爲我們的主頁一個簡單的控制器。
package com.yiibai.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/")
public class IndexController {
@RequestMapping(method = RequestMethod.GET)
public String getIndexPage() {
return "UserManagement";
}
}
創建基於Spring事務來處理用戶的相關操作
package com.yiibai.springmvc.service;
import java.util.List;
import com.yiibai.springmvc.model.User;
public interface UserService {
User findById(long id);
User findByName(String name);
void saveUser(User user);
void updateUser(User user);
void deleteUserById(long id);
List<User> findAllUsers();
void deleteAllUsers();
public boolean isUserExist(User user);
}
package com.yiibai.springmvc.service;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.yiibai.springmvc.model.User;
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService{
private static final AtomicLong counter = new AtomicLong();
private static List<User> users;
static{
users= populateDummyUsers();
}
public List<User> findAllUsers() {
return users;
}
public User findById(long id) {
for(User user : users){
if(user.getId() == id){
return user;
}
}
return null;
}
public User findByName(String name) {
for(User user : users){
if(user.getUsername().equalsIgnoreCase(name)){
return user;
}
}
return null;
}
public void saveUser(User user) {
user.setId(counter.incrementAndGet());
users.add(user);
}
public void updateUser(User user) {
int index = users.indexOf(user);
users.set(index, user);
}
public void deleteUserById(long id) {
for (Iterator<User> iterator = users.iterator(); iterator.hasNext(); ) {
User user = iterator.next();
if (user.getId() == id) {
iterator.remove();
}
}
}
public boolean isUserExist(User user) {
return findByName(user.getUsername())!=null;
}
public void deleteAllUsers(){
users.clear();
}
private static List<User> populateDummyUsers(){
List<User> users = new ArrayList<User>();
users.add(new User(counter.incrementAndGet(),"Sam", "NY", "[email protected]"));
users.add(new User(counter.incrementAndGet(),"Tomy", "ALBAMA", "[email protected]"));
users.add(new User(counter.incrementAndGet(),"Kelly", "NEBRASKA", "[email protected]"));
return users;
}
}
創建模型 - Model
package com.yiibai.springmvc.model;
public class User {
private long id;
private String username;
private String address;
private String email;
public User(){
id=0;
}
public User(long id, String username, String address, String email){
this.id = id;
this.username = username;
this.address = address;
this.email = email;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime \* result + (int) (id ^ (id >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof User))
return false;
User other = (User) obj;
if (id != other.id)
return false;
return true;
}
@Override
public String toString() {
return "User \[id=" + id + ", username=" + username + ", address=" + address
+ ", email=" + email + "\]";
}
}
創建Spring配置文件類
package com.yiibai.springmvc.configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.yiibai.springmvc")
public class HelloWorldConfiguration extends WebMvcConfigurerAdapter{
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
registry.viewResolver(viewResolver);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/\*\*").addResourceLocations("/static/");
}
}
創建Spring初始化器類
看看我們是如何註冊CORS過濾器,Spring配置,這將幫助我們避免同源策略的問題。
package com.yiibai.springmvc.configuration;
import javax.servlet.Filter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class HelloWorldInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>\[\] getRootConfigClasses() {
return new Class\[\] { HelloWorldConfiguration.class };
}
@Override
protected Class<?>\[\] getServletConfigClasses() {
return null;
}
@Override
protected String\[\] getServletMappings() {
return new String\[\] { "/" };
}
@Override
protected Filter\[\] getServletFilters() {
Filter \[\] singleton = { new CORSFilter() };
return singleton;
}
}
創建過濾器處理同源策略相關的問題
package com.yiibai.springmvc.configuration;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
System.out.println("Filtering on...........................................................");
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "\*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
部署和運行
現在構建war(無論是在Eclipse中,如提到的前面的教程)或通過Maven的命令行(mvn clean install)。部署 war 到Servlet3.0容器。
打開瀏覽器,瀏覽:http://localhost:8080/Spring4MVCAngularJSExample/
詳細填寫添加一個新用戶
點擊添加,用戶應該以異步方式添加。
點擊刪除用戶,用戶應異步刪除,這裏刪除了第一條。
點擊編輯的用戶,用戶的詳細信息應顯示在表格。並根據需要更新詳細信息。
現在點擊更新,用戶應異步更新。
由於AngularJS表單驗證,如果試圖不按要求提供輸入,你會看到驗證錯誤如下圖所示。
就這樣(包教不包會)!