實例概述
本文提供一個使用阿里Ice前端框架(封裝React)與服務Spring Boot項目進行相互通信的項目實例。具體項目環境可參見:阿里ICE前端工程創建過程。該實例中不對Spring Boot項目的創建和使用進行介紹,僅提供相應的Controller方法作爲與React前端工程通信的API。該實例具有的相關組件組成的頁面如下:
- UserCreateForm:提供表單信息進行Post請求創建用戶。
- UserListTable:用於獲取數據庫中的user列表並展示。
- UserDetailForm:用戶渲染指定用戶的詳細信息並提供修改的功能。
以上三個組件的關係爲並列(沒有進行組合),但其中存在一些頁面跳轉的關係,後續會進行說明。
在該實例中重點進行關注和介紹的問題點爲:
- React與Spring Boot前後端的Get/Post請求交互方式如何實現?
- ice中頁面之間的跳轉如何實現?並如何攜帶和獲取參數?
- React中props和state的使用。
- React如何使用map渲染列表?
- 服務端跨域請求的的設置。
項目代碼:https://github.com/Yitian-Zhang/my-ice-start。下面來看具體的實例實現過程。
服務端Controller和跨域問題設置
服務端方面已經使用Spring Boot+MyBatis+MySQL搭建好項目環境,下面創建ReactUserController類提供如下的接口方法,並通過測試各接口功能正常。
/**
* React(my-ice-start)項目接口Controller
* @author yitian
*/
@RestController
@RequestMapping("/react-user")
public class ReactUserController {
@Autowired
private UserService userService;
@RequestMapping("/detail")
public CommonResult getUserDetail(Long id) {
if (id == null) {
return CommonResult.error("用戶ID不能爲空");
}
return new CommonResult(true, 200, userService.getUserById(id));
}
@RequestMapping("/create")
public CommonResult createUser(@RequestBody User user) {
if (user == null) {
return CommonResult.error("添加用戶爲空");
}
System.out.println(user);
int result = userService.insertUser(user);
boolean success = result == 1;
String msgInfo = success ? "添加成功" : "添加失敗";
return new CommonResult(success, msgInfo, user);
}
@RequestMapping("/list")
public CommonResult userList() {
List<User> userList = userService.getUserList();
return new CommonResult(true, "獲取成功", userList);
}
@RequestMapping(value = "/update", method = RequestMethod.POST)
public CommonResult updateUser(@RequestBody User user) {
if (user == null || user.getId() == null) {
return CommonResult.error("待更新用戶信息爲空");
}
System.out.println(user);
int result = userService.updateUserById(user);
boolean success = result == 1;
String msg = success ? "更新成功" : "更新失敗";
return new CommonResult(success, msg, userService.getUserById(user.getId()));
}
@RequestMapping("/delete")
public CommonResult deleteUser(Long id) {
if (id == null) {
return CommonResult.error("UserId不能爲空");
}
int result = userService.deleteUser(id);
boolean success = result == 1;
String msg = success ? "刪除成功" : "刪除失敗";
return new CommonResult(success, msg, userService.getUserList());
}
}
此外由於Spring Boot項目爲localhost:8080,而ice啓動項目地址爲localhost:3333,所以在前後端項目進行通信時會存在跨域問題,下面在Spring Boot項目中加入如下的Bean,來配置請求的response返回頭,使其允許所有的request:
/**
* 解決React跨域請求時的異常
*/
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
};
}
至此該實例中的Spring Boot服務端項目的方法已經開發完成,後面重點關注ICE項目中如何進行前後端項目的交互。
創建並添加用戶
首先進行createUser的頁面開發,這裏定義的組件如下:
import React, {Component} from 'react';
import {withRouter} from "react-router-dom";
import PropTypes from 'prop-types';
import axios from 'axios';
@withRouter
class UserCreateForm extends Component {
// 頁面跳轉靜態屬性
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.state = {
username: '',
sex: 'MALE',
note: ''
};
}
// 表單變更處理函數
handleChange = (event) => {
const {name, value} = event.target;
this.setState({
[name] : value,
})
};
// 創建用戶信息
handleSubmit = (event) => {
this.submitUser();
};
// post請求提交更新後的user信息
submitUser() {
const {username, sex, note} = this.state;
const { history } = this.props;
console.log(username + ", " + sex + ", " + note);
// 直接使用post請求
axios.post('http://localhost:8080/react-user/create', {
// id: id,
userName: username,
sex: sex, // 這裏可以直接根據SexEnum的枚舉name來進行參數傳遞,不需要使用枚舉key
note: note
})
.then(function (response) {
console.log(response);
alert(response.data.msgInfo);
// 添加完成後跳轉list頁面
history.push({
pathname: '/user/list',
});
})
.catch(function (error) {
console.log(error);
});
};
render() {
const {username, sex, note} = this.state;
return (
<React.Fragment>
<h1>User Detail Form</h1>
<form>
<table>
<tr>
<td>Username:</td>
<td><input
type="text"
id="username"
name="username"
value={username}
onChange={this.handleChange}/></td>
</tr>
<tr>
<td>sex:</td>
<td><select
name="sex"
value={sex}
onChange={this.handleChange}>
<option value="MALE">MALE</option>
<option value="FEMALE">FEMALE</option>
</select></td>
</tr>
<tr>
<td>note:</td>
<td><input
type="text"
id="note"
name="note"
value={note}
onChange={this.handleChange}/></td>
</tr>
<tr>
<td><input
type="button"
value="CreateUser"
onClick={this.handleSubmit}/></td>
</tr>
</table>
</form>
</React.Fragment>
)
}
}
export default UserCreateForm;
該部分代碼中有如下幾個方面需要重點關注:
1. 在submitUser方法中使用axios進行post請求。
ice允許使用自己封裝的request進行請求的發送(其實就是封裝的axios),也允許使用ajax、jquery,axios等方式發送請求。這裏使用了axios的方式進行post的請求的發送。格式如下:
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
或者使用axios(config {...}) 的格式發送post請求:
// Send a POST request
axios({
method: 'post',
url: '/user/12345',
data: { // 這裏data中的參數爲requestBody參數,服務端需要使用@RequestBody註解進行獲取
firstName: 'Fred',
lastName: 'Flintstone'
}
}).then(function (response) {
console.log(response);
}).catch(function (error) {
console.log(error);
});
2. 用戶創建成功後如何進行的頁面跳轉(跳轉頁面代碼的實現在後面)。
對於頁面的跳轉過程的實現,這裏使用的爲阿里Ice中實現組件和頁面間跳轉並進行參數傳遞中提到的,withRouter方式進行實現。
頁面實現完成後,顯示如下,在後續跳轉頁面完成後,進行完整的集成測試。
用戶列表和管理
用戶列表渲染
上面在創建用戶完成後,頁面會跳轉到顯示數據庫中所有用戶的列表頁面中,該列表頁面使用如下的組件進行實現:
import React, {Component} from 'react';
import axios from 'axios';
import {List} from "@alifd/next";
import UserDetailForm from "../UserDetailForm";
import {withRouter} from "react-router-dom";
import PropTypes from 'prop-types';
import './index.css';
@withRouter
class UserListTable extends Component {
// 頁面跳轉靜態配置
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.state = {
userList: [],
};
}
componentDidMount() {
this.getUserList();
}
// 獲取用戶列表數據
async getUserList() {
try {
const response = await axios.get('http://localhost:8080/react-user/list');
console.log(response);
this.setState({
userList: response.data.data,
})
} catch (error) {
console.error(error);
}
}
// 詳情和更新頁面
handleClickDetail = (id) => {
console.log("ListTable id: " + id);
// 頁面跳轉
const { history } = this.props;
history.push({
pathname: '/user/detail',
state: { id },
});
};
// 刪除數據
handleClickDelete = (id) => {
this.deleteUser(id);
};
// 刪除用戶
async deleteUser(id) {
try {
const response = await axios.get('http://localhost:8080/react-user/delete?id=' + id);
console.log(response);
alert(response.data.msgInfo);
this.setState({
userList: response.data.data,
});
} catch (e) {
console.error(e);
}
}
render() {
const {userList} = this.state;
return (
<div>
<h1>User List</h1>
<table>
<thead>
<tr>
<td>Id</td>
<td>UserName</td>
<td>Sex</td>
<td>Note</td>
<td>Operate</td>
</tr>
</thead>
<tbody>
{
userList.map((row, index) => {
const id = row.id;
return (
<tr key={index}>
<td>{row.id}</td>
<td>{row.userName}</td>
<td>{row.sex}</td>
<td>{row.note}</td>
<td>
<button
className="listButton"
onClick={() => this.handleClickDetail(id)}>Detail</button>
<button
className="listButton"
onClick={() => this.handleClickDelete(id)}>Delete</button>
</td>
</tr>
)
})
}
</tbody>
</table>
</div>
);
}
}
export default UserListTable;
對於上述的代碼,需要關注的重點如下:
1. 如何使用axios的GET請求來操作數據?
在使用axios進行GET請求時,與POST請求類似,有如下兩種方式。第一種爲直接使用封裝的axios.get進行請求,格式如下:
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
// update state or do something
this.setState({
// ...
})
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
// Optionally the request above could also be done as
axios.get('/user', {
params: { // 這裏的參數設置爲URL參數(根據URL攜帶參數)
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
// Want to use async/await? Add the `async` keyword to your outer function/method.
async function getUser() {
try {
const response = await axios.get('/user?ID=12345');
console.log(response);
} catch (error) {
console.error(error);
}
}
第二種爲使用axios(config {...}) 的方式發送GET請求:
axios({
method: 'get',
url: 'http://bit.ly/2mTM3nY',
params: {
id: id,
}
})
.then(function (response) {
console.log(response);
});
2. 如何在頁面跳轉時傳遞需要的參數?
在頁面跳轉時傳遞參數,需要在history.push方法中進行如下設置:
// 頁面跳轉
const { history } = this.props;
history.push({
pathname: '/user/detail',
state: { id },
});
之後在用戶詳情頁面就可以通過如下的方式獲取該傳遞的id參數:
componentDidMount() {
let id = this.props.location.state.id;
console.log("DetailForm id: " + id);
}
3. 如何根據userList渲染出用戶的整個列表頁面?
在對userList進行循環渲染整個user列表時,需要首先在constructor中對userList整個state進行初始化爲數組:
constructor(props) {
super(props);
this.state = {
userList: [],
};
}
然後在render方法中使用map方法對數組進行遍歷並渲染列表中的數據:
<tbody>
{
userList.map((row, index) => {
const id = row.id;
return (
<tr key={index}>
<td>{row.id}</td>
<td>{row.userName}</td>
<td>{row.sex}</td>
<td>{row.note}</td>
<td>
<button
className="listButton"
onClick={() => this.handleClickDetail(id)}>Detail</button>
<button
className="listButton"
onClick={() => this.handleClickDelete(id)}>Delete</button>
</td>
</tr>
)
})
}
</tbody>
完成後該頁面顯示如下 :
對於每個用戶,其中都對應了detail和delete按鈕。其中detail會跳轉到UserDetail頁面來顯示該用戶的具體信息,delete按鈕則是對該用戶信息進行刪除。下面先看一下簡單的delete方法的實現,detail的實現在下一節中進行說明。
用戶刪除實現
上述列表中delete用戶時,同樣是使用axios.get請求來刪除數據庫中的用戶數據,然後獲取新的userList返回值並使用setState方法更新state,使該頁面重新渲染。具體的代碼已經在上面組件代碼中給出,實現比較簡單。
用戶詳情信息以及修改
用戶詳情頁爲userList頁面中對指定user點擊detail按鈕而跳轉得到的頁面,該頁面組件的實現如下:
import React, {Component} from 'react';
import axios from 'axios';
import {request} from "../../../.ice";
import {withRouter} from 'react-router-dom';
import PropTypes from 'prop-types';
@withRouter
class UserDetailForm extends Component {
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.state = {
id: '',
username: '',
sex: '',
note: ''
};
}
componentDidMount() {
// 使用axios進行get和post請求
let id = this.props.location.state.id;
console.log("DetailForm id: " + id);
this.getUserByAxios(id);
}
// 使用axios來進行get請求
// 使用前需要安裝axios:npm install axios --save,並進行import
async getUserByAxios(id) {
try {
const response = await axios.get("http://localhost:8080/react-user/detail?id=" + id);
console.log(response);
const user = response.data.data;
this.setState({
id: user.id,
username: user.userName,
sex: user.sex,
note: user.note
})
} catch (error) {
console.error(error);
}
}
// 表單變更處理函數
handleChange = (event) => {
const {name, value} = event.target;
this.setState({
[name] : value,
})
};
// 更新用戶信息函數
handleSubmit = (event) => {
this.submitUser();
};
// post請求提交更新後的user信息
submitUser() {
const {id, username, sex, note} = this.state;
console.log(id + ", " + username + ", " + sex + ", " + note);
axios.post('http://localhost:8080/react-user/update', {
id: id,
userName: username,
sex: sex,
note: note
})
.then(function (response) {
console.log(response);
alert(response.data.msgInfo);
// 更新列表state
const user = response.data.data;
this.setState({
id: user.id,
username: user.userName,
sex: sex,
note: note
});
})
.catch(function (error) {
console.log(error);
});
};
render() {
const {id, username, sex, note} = this.state;
return (
<React.Fragment>
<h1>User Detail Form</h1>
<form>
<table>
<tr>
<td>Id:</td>
<td><input
type="text"
id="id"
name="id"
value={id}
disabled="true"
onChange={this.handleChange}/></td>
</tr>
<tr>
<td>Username:</td>
<td><input
type="text"
id="username"
name="username"
value={username}
onChange={this.handleChange}/></td>
</tr>
<tr>
<td>sex:</td>
<td><select
name="sex"
value={sex}
onChange={this.handleChange}>
<option value="MALE">MALE</option>
<option value="FEMALE">FEMALE</option>
</select></td>
</tr>
<tr>
<td>note:</td>
<td><input
type="text"
id="note"
name="note"
value={note}
onChange={this.handleChange}/></td>
</tr>
<tr>
<td><input
type="button"
value="UpdateUser"
onClick={this.handleSubmit}/></td>
</tr>
</table>
</form>
</React.Fragment>
)
}
}
export default UserDetailForm;
該部分代碼中同時用到了axios.get請求來根據頁面跳轉傳入的id參數,獲取該用戶對應的詳細信息,同時使用axios.post請求在處理對用戶信息的更新操作。以此實現了用戶詳細信息頁面的展示,以及用戶詳情信息的更新功能。
該頁面的顯示爲:
Ice項目路由配置
上述代碼中在進行頁面跳轉時,使用到了如下的請求路徑:
/user/list
/user/detail
在ice項目中,需要在routes.j[t]s路由配置文件中來進行聲明式路由的配置:
const routes = [
{
path: '/user/list',
component: UserListTable,
},
{
path: '/user/detail',
component: UserDetailForm,
},
...
];
export default routes;
以上即爲該實例的全部實現。END。