大家好!我希望你一切安好?
我目前正在开发一个简单的应用程序,以了解有关Spring Boot、Spring Security性、jpa和服务器端分页的更多信息。
我成功地构建了我的API,并且我有一个使用分页的控制器。为了使用这个API,我开发了一个反应应用程序,一切都可以按照我想要的方式工作(2FA,帐户创建,登录,忘记密码,更改密码,...)。
但现在,我想开发一个管理界面,在其中我希望能够管理用户帐户。
所以,我创建了一个控制器,它需要管理员角色和连接。为了返回用户列表,我使用了一个扩展分页和排序存储库的DAO,它工作得很好!
现在我想在react应用程序中实现这种分页,这就是我遇到的问题。
我为react尝试了许多分页库。但它们都需要恢复单个列表中的所有数据,这是我不想要的。所以,我开始开发自己的分页组件。我可以轻松地进行正面分页,但只能访问第一页、最后一页、上一页和下一页,但我无法添加这样的页面选择按钮:13[…][n-2][n-1][n]。
这是我的代码:
使用者JAVA
@Entity
@Table(
name = "USER",
uniqueConstraints = {
@UniqueConstraint(columnNames = "username"),
@UniqueConstraint(columnNames = "email")
}
)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
@Size(min = 3, max = 16)
private String username;
@NaturalId
@NotBlank
@Email
private String email;
@NotBlank
@Size(max = 100)
private String password;
private boolean isUsingTwoFA;
private String twoFASecret;
@Column(columnDefinition = "DATE NOT NULL")
private LocalDate accountCreationDate;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "USER_ROLE",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
}
UserDto。JAVA
public class UserDto {
private String username;
private String email;
private String accountCreationDate;
private boolean isUsingTwoFA;
List<String> roles;
}
PagedUserRepository.java
public interface PagedUserRepository extends PagingAndSortingRepository<User, Long> {
Page<User> findAll(Pageable pageable);
}
仪表板用户服务。JAVA
@Service
public class DashboardUsersService {
private UserRepository userRepository;
private PagedUserRepository pagedUserRepository;
@Autowired
public DashboardUsersService(UserRepository userRepository, PagedUserRepository pagedUserRepository) {
this.userRepository = userRepository;
this.pagedUserRepository = pagedUserRepository;
}
public ResponseEntity<?> getUsers(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
Page<User> usersPage = pagedUserRepository.findAll(pageable);
if (usersPage.getContent().isEmpty()) {
return new ResponseEntity<>(new ApiResponseDto(false, "Unable to retrieve any user"), HttpStatus.INTERNAL_SERVER_ERROR);
}
final List<UserDto> users = usersPage.getContent()
.stream()
.map(UserDto::new)
.collect(Collectors.toList());
return new ResponseEntity<>(new PagedResponseDto(users, usersPage), HttpStatus.OK);
}
}
仪表板用户控制器。JAVA
@CrossOrigin(maxAge = 36000)
@RestController
@RequestMapping(path = "/api/secure/admin/dashboard/users")
public class DashboardUsersController {
@Autowired
private DashboardUsersService dashboardUsersService;
@Secured("ROLE_ADMIN")
@GetMapping
public ResponseEntity<?> getUsers(
@RequestParam(value = "page", defaultValue = "0") int page,
@RequestParam(value = "size", defaultValue = "10") int size
) {
return dashboardUsersService.getUsers(page, size);
}
}
你sers.js
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import {getPageUsers} from "../../../../api/AdminApi";
import UserTableLine from "./component/UserTableLine";
import UserPagination from "./component/UserPagination";
class Users extends Component{
state = {
pagedResponse: {},
users: [],
showLoading: false
};
constructor(props){
super(props);
this.getFirstPageUsers = this.getFirstPageUsers.bind(this);
this.handleChangePage = this.handleChangePage.bind(this);
}
componentDidMount(){
document.title = "Users management";
this.getFirstPageUsers();
}
getFirstPageUsers(){
const defaultPageable = {
pageNumber: 0
};
this.setState({showLoading: true});
getPageUsers(defaultPageable).then(res => {
this.setState({
pagedResponse: res,
users: res.content,
showLoading: false
});
}).catch(error => {
if(error.message && error.success === false){
this.props.showAlert(error.message, "error");
} else {
this.props.showAlert("Sorry! Something went wrong. Please try again!", "error");
}
this.setState({showLoading: false});
console.log(error);
});
}
handleChangePage(pageable){
this.setState({showLoading: true});
getPageUsers(pageable).then(res => {
this.setState({
pagedResponse: res,
users: res.content,
showLoading: false
});
}).catch(error => {
if(error.message && error.success === false){
this.props.showAlert(error.message, "error");
} else {
this.props.showAlert("Sorry! Something went wrong. Please try again!", "error");
}
this.setState({showLoading: false});
console.log(error);
});
}
render(){
let tableLines = [];
if(this.state.pagedResponse && this.state.users.length > 0){
tableLines = Object.keys(this.state.users)
.map(key => <UserTableLine key={key} user={this.state.users[key]}/>);
}
return(
<div>
<h1>Users <span className="text-muted" style={{fontSize: 11}}>management</span></h1>
<hr/>
{
this.state.showLoading
?
<div className="align-content-center text-center">
<h4 className="text-muted">Loading. Please Wait...</h4>
<i className="material-icons w3-xxxlarge w3-spin align-content-center">refresh</i>
</div>
:
<div>
<table className="table table-hover">
<thead>
<tr>
<th scope="col">Avatar</th>
<th scope="col">Username</th>
<th scope="col">email</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
{tableLines}
</tbody>
</table>
<UserPagination
showAlert={this.props.showAlert}
page={this.state.pagedResponse}
handleChangePage={this.handleChangePage}
/>
</div>
}
</div>
);
}
}
export default withRouter(Users);
用户表格行。js
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import {Modal, ModalBody, ModalHeader} from 'reactstrap';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
import {faSearch} from '@fortawesome/free-solid-svg-icons';
class UserTableLine extends Component {
state = {
showModalUserInfo: false,
user: {}
};
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
}
componentDidMount() {
this.setState({
user: this.props.user
});
}
toggle() {
this.setState({
showModalUserInfo: !this.state.showModalUserInfo
});
}
render() {
let roles;
if (this.state.user && this.state.user.roles) {
roles = Object.keys(this.state.user.roles).map(
key => " " + this.state.user.roles[key]
);
}
return (
<tr>
<th scope="row">
<img src={"http://cravatar.eu/helmavatar/" + this.state.user.username + "/32.png"}
alt={this.state.user.username} className="img-fluid"/>
</th>
<th>
{this.state.user.username}
</th>
<th>
{this.state.user.email}
</th>
<th>
<button className="btn btn-dark" onClick={this.toggle}><FontAwesomeIcon icon={faSearch}/></button>
</th>
<Modal isOpen={this.state.showModalUserInfo} toggle={this.toggle} className={this.props.className}>
<ModalHeader toggle={this.toggle}>
<div className="align-content-center align-items-center align-self-center text-center">
<img src={"http://cravatar.eu/helmavatar/" + this.state.user.username + "/50.png"}
alt={this.state.user.username} className="img-fluid rounded align-self-center"/>
{" " + this.state.user.username + ' { ' + roles + ' }'}
</div>
</ModalHeader>
<ModalBody>
<p>
<b>Email adresse:</b> {this.state.user.email}
</p>
<p>
<b>Account creation date:</b> {this.state.user.accountCreationDate}
</p>
<p>
<b>2FA status:</b>
{
this.state.user.usingTwoFA
?
<span className="badge badge-success">enabled</span>
:
<span className="badge badge-danger">disabled</span>
}
</p>
</ModalBody>
</Modal>
</tr>
);
}
}
export default withRouter(UserTableLine);
最后是用户分页。js
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
class UserPagination extends Component {
state = {
pagination: {}
};
constructor(props) {
super(props);
this.onPageChange = this.onPageChange.bind(this);
this.goToFirstPage = this.goToFirstPage.bind(this);
this.goToLastPage = this.goToLastPage.bind(this);
this.goToPreviousPage = this.goToPreviousPage.bind(this);
this.goToNextPage = this.goToNextPage.bind(this);
this.setStatePromise = this.setStatePromise.bind(this);
}
componentDidMount() {
const pagination = {
firstPage: this.props.page.firstPage,
lastPage: this.props.page.lastPage,
currentPageable: {
sort: {
sorted: false,
unsorted: true
},
offset: this.props.page.offset,
pageSize: this.props.page.pageSize,
pageNumber: this.props.page.number
},
previousPageable: this.props.page.previousPageable,
nextPageable: this.props.page.nextPageable,
totalPages: this.props.page.totalPages,
totalElement: this.props.page.totalElement
};
this.setState({pagination});
}
setStatePromise(newState) {
return new Promise((resolve) => {
this.setState(newState, () => {
resolve();
});
});
}
onPageChange = (pageable) => {
this.props.handleChangePage(pageable);
};
goToFirstPage() {
const firstPage = {
sort: {
sorted: false,
unsorted: true
},
offset: 0,
pageSize: 10,
pageNumber: 0
};
this.onPageChange(firstPage);
}
goToLastPage() {
const lastPage = {
sort: {
sorted: false,
unsorted: true
},
pageSize: 10,
pageNumber: this.state.pagination.totalPages - 1
};
this.onPageChange(lastPage);
}
goToPreviousPage() {
const previousPage = this.state.pagination.previousPageable;
if (previousPage !== "INSTANCE") {
this.onPageChange(previousPage);
}
}
goToNextPage() {
const nextPage = this.state.pagination.nextPageable;
if (nextPage !== "INSTANCE") {
this.onPageChange(nextPage);
}
}
getPagesNumberButtons(){
let pages = [];
if (this.state.pagination) {
pages.push(
<li key={1} className="page-item active">
<p className="page-link">{this.state.pagination.currentPageable.pageNumber}</p>
</li>
);
}
return pages;
}
render() {
return (
<div>
<ul className="pagination">
<li className="page-item" onClick={this.goToFirstPage}>
<p className="page-link">«</p>
</li>
<li className="page-item" onClick={this.goToPreviousPage}>
<p className="page-link">Prev</p>
</li>
{this.getPagesNumberButtons}
<li id="nextPage" className="page-item" onClick={this.goToNextPage}>
<p className="page-link">Next</p>
</li>
<li id="lastPage" className="page-item" onClick={this.goToLastPage}>
<p className="page-link">»</p>
</li>
</ul>
</div>
);
}
}
export default withRouter(UserPagination);
但是如果你想拥有完整的代码:Github
如果你有办法解决我的问题,或者你知道一个处理服务器端分页的库,我很感兴趣:)
提前感谢你花时间阅读这一切,更感谢你的帮助。
亚历克西斯
编辑1:以下是我从控制器得到的响应:
{
"content": [
{
"username": "test1",
"email": "test1@gmail.com",
"accountCreationDate": "2018-08-22",
"roles": [
"USER"
],
"usingTwoFA": false
},
{
"username": "test2",
"email": "test2@gmail.com",
"accountCreationDate": "2018-08-22",
"roles": [
"USER"
],
"usingTwoFA": false
},
{
"username": "test3",
"email": "test3@gmail.com",
"accountCreationDate": "2018-08-22",
"roles": [
"USER"
],
"usingTwoFA": false
},
{
"username": "test4",
"email": "test4@gmail.com",
"accountCreationDate": "2018-08-22",
"roles": [
"USER"
],
"usingTwoFA": false
},
{
"username": "test5",
"email": "test5@gmail.com",
"accountCreationDate": "2018-08-22",
"roles": [
"USER"
],
"usingTwoFA": false
},
{
"username": "test6",
"email": "test6@gmail.com",
"accountCreationDate": "2018-08-22",
"roles": [
"USER"
],
"usingTwoFA": false
},
{
"username": "test7",
"email": "test7@gmail.com",
"accountCreationDate": "2018-08-22",
"roles": [
"USER"
],
"usingTwoFA": false
},
{
"username": "test8",
"email": "test8@gmail.com",
"accountCreationDate": "2018-08-22",
"roles": [
"USER"
],
"usingTwoFA": false
},
{
"username": "test9",
"email": "test9@gmail.com",
"accountCreationDate": "2018-08-22",
"roles": [
"USER"
],
"usingTwoFA": false
},
{
"username": "test10",
"email": "test10@gmail.com",
"accountCreationDate": "2018-08-22",
"roles": [
"USER"
],
"usingTwoFA": false
}
],
"offset": 0,
"pageNumber": 0,
"pageSize": 10,
"lastPage": false,
"totalElement": 24,
"totalPages": 3,
"size": 10,
"number": 0,
"numberOfElements": 10,
"firstPage": true,
"previousPageable": "INSTANCE",
"nextPageable": {
"sort": {
"sorted": false,
"unsorted": true
},
"offset": 10,
"pageSize": 10,
"pageNumber": 1,
"paged": true,
"unpaged": false
}
}
在与@SGhaleb进行交互之后,我终于为这种分页开发了自己的组件,并且它可以正常工作。这不是一个最优的解决方案(1k行代码),但对于第一个版本来说已经足够了,我将在以后对其进行优化。
下面是所述组件的代码:
import React, {Component} from 'react';
class UserPagination extends Component {
constructor(props) {
super(props);
this.state = {
page: props.page,
pageSize: props.pageSize,
currentPage: props.currentPage,
totalNumberOfElements: props.totalNumberOfElements
};
this.onPageChange = this.onPageChange.bind(this);
this.goToFirstPage = this.goToFirstPage.bind(this);
this.goToLastPage = this.goToLastPage.bind(this);
this.goToPreviousPage = this.goToPreviousPage.bind(this);
this.goToNextPage = this.goToNextPage.bind(this);
this.buildPagination = this.buildPagination.bind(this);
}
onPageChange = (pageNumber) => {
this.props.handleChangePage(pageNumber);
};
static getDerivedStateFromProps(props, state) {
state = props;
return state;
}
goToFirstPage() {
this.onPageChange(0);
}
goToLastPage() {
this.onPageChange(this.state.page.totalNumberOfPages - 1);
}
goToPreviousPage() {
const previousPage = this.state.page.previousPageable;
if (previousPage !== "INSTANCE") {
this.onPageChange(previousPage.pageNumber);
}
}
goToNextPage() {
const {currentPage, page} = this.state;
const nextPage = page.nextPageable;
if (nextPage !== "INSTANCE") {
this.onPageChange(currentPage + 1);
}
}
buildPagination(page, currentPage) {
//PAGINATION LOGIC
//SEE LINK TO PASTEBIN.COM
}
render() {
const {page, currentPage} = this.state;
let pagination = this.buildPagination(page, currentPage);
return (
<ul className="pagination">
{pagination}
</ul>
);
}
}
export default UserPagination;
https://pastebin.com/x4Fx9pLm
现在分页看起来就像我想要的:
我正在尝试使用cassandra实现分页,但我在Stackoverflow上没有得到任何成功的解决方案。我遇到的突出错误是“对第一个页面以外的页面进行分页查询需要具有有效分页状态的CassandraPageRequest”。请协助。
本文向大家介绍SpringBoot 使用Mybatis分页插件实现详解,包括了SpringBoot 使用Mybatis分页插件实现详解的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了SpringBoot 使用Mybatis分页插件实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1、导入分页插件包和jpa包 2、增加分页配置 配
我有以下的请求给管理员和管理员 我想用上面Dto中不为空的字段搜索用户存储库。 示例:如果firstName是“john”,lastName是“smith”,其余字段为空,那么我必须在存储库中搜索firstName是“john”,lastName是“smith”的所有记录(不是或) 我还必须为响应执行分页 我是Spring靴新手,有人能建议我怎么做吗?
本文向大家介绍Springboot分页插件使用实例解析,包括了Springboot分页插件使用实例解析的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了Springboot分页插件使用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在springboot工程下的pom.xml中添加依赖 在工程的配置Application文件中添
本文向大家介绍SpringBoot整合mybatis结合pageHelper插件实现分页,包括了SpringBoot整合mybatis结合pageHelper插件实现分页的使用技巧和注意事项,需要的朋友参考一下 SpringBoot整合mybatis分页操作 SpringBoot整合Mybatis进行分页操作,这里需要使用Mybatis的分页插件:pageHelper, 关于pageHelper的
我很感激任何帮助如何解决这个问题。