Example of front and back communication between Alice and Spring Boot

Case summary

This article provides a project instance that uses aliice front-end framework (encapsulating React) to communicate with Spring Boot project. For specific project environment, please refer to: Alibaba ICE front end project creation process . This example does not introduce the creation and use of Spring Boot project, but only provides the corresponding Controller method as the API to communicate with the React front-end project. The related components of this instance consist of the following pages:

  • UserCreateForm: provide form information for Post request to create users.
  • UserListTable: used to get the user list in the database and display it.
  • UserDetailForm: the user renders the details of the specified user and provides the function of modification.

The above three components are in parallel (not combined), but there are some page Jump relationships, which will be explained later.

In this example, the key points of attention and introduction are as follows:

  • How to realize the Get/Post request interaction between React and Spring Boot?
  • How to realize the jump between pages in ice? How to carry and obtain parameters?
  • Use of props and state in React.
  • How does React render lists with map s?
  • Settings for cross domain requests from the server.

Project code: https://github.com/Yitian-Zhang/my-ice-start . Let's take a look at the specific instance implementation process.

Server Controller and cross domain problem settings

The server side has used Spring Boot+MyBatis+MySQL to build the project environment. Create the reactuercontroller class below to provide the following interface methods, and test that each interface function is normal.

/**
 * React(my-ice-start)Project interface 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("user ID Can not be empty");
        }
        return new CommonResult(true, 200, userService.getUserById(id));
    }

    @RequestMapping("/create")
    public CommonResult createUser(@RequestBody User user) {
        if (user == null) {
            return CommonResult.error("Add user is empty");
        }
        System.out.println(user);

        int result = userService.insertUser(user);
        boolean success = result == 1;
        String msgInfo = success ? "Add success" : "Add failure";
        return new CommonResult(success, msgInfo, user);
    }


    @RequestMapping("/list")
    public CommonResult userList() {
        List<User> userList = userService.getUserList();
        return new CommonResult(true, "Get success", userList);
    }


    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public CommonResult updateUser(@RequestBody User user) {
        if (user == null || user.getId() == null) {
            return CommonResult.error("The user information to be updated is empty");
        }
        System.out.println(user);

        int result = userService.updateUserById(user);
        boolean success = result == 1;
        String msg = success ? "Update success" : "Update failed";
        return new CommonResult(success, msg, userService.getUserById(user.getId()));
    }

    @RequestMapping("/delete")
    public CommonResult deleteUser(Long id) {
        if (id == null) {
            return CommonResult.error("UserId Can not be empty");
        }
        int result = userService.deleteUser(id);
        boolean success = result == 1;
        String msg = success ? "Delete successful" : "Delete failed";
        return new CommonResult(success, msg, userService.getUserList());
    }
}

In addition, because the Spring Boot project is localhost:8080, and the ice boot project address is localhost:3333, there will be cross domain problems in the communication between the front and rear projects. Add the following Bean to the Spring Boot project to configure the response return header of the request to allow all requests:

    /**
     * Resolve exceptions in React cross domain requests
     */
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedOrigins("*");
            }
        };
    }

So far, the method of the Spring Boot server project in this instance has been developed. Later, we will focus on how to interact with the front and back projects in the ICE project.

Create and add users

First, develop the createUser page. The components defined here are as follows:

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 {
  // Page Jump static properties
  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = {
      username: '',
      sex: 'MALE',
      note: ''
    };
  }

  // Form change processing function
  handleChange = (event) => {
    const {name, value} = event.target;
    this.setState({
      [name] : value,
    })
  };

  // Create user information
  handleSubmit = (event) => {
    this.submitUser();
  };

  // post requests to submit updated user information
  submitUser() {
    const {username, sex, note} = this.state;
    const { history } = this.props;
    console.log(username + ", " + sex + ", " + note);

    // Use post request directly
    axios.post('http://localhost:8080/react-user/create', {
      // id: id,
      userName: username,
      sex: sex, // Here, you can directly pass parameters according to the enumeration name of SexEnum, without using enumeration key
      note: note
    })
      .then(function (response) {
        console.log(response);
        alert(response.data.msgInfo);

        // Jump to list page after adding
        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;

In this part of the code, the following aspects need to be focused on:

1. Use axios in the submitUser method for post request.

ice allows you to use your encapsulated request to send requests (in fact, the encapsulated axios). It also allows you to use ajax, jquery, axios and other methods to send requests. Here, axios is used to send the post request. The format is as follows:

axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

Or send the post request in the form of axios(config {...}):

    // Send a POST request
    axios({
      method: 'post',
      url: '/user/12345',
      data: { // Here, the parameter in data is the RequestBody parameter. The server needs to use the @ RequestBody annotation to get it
        firstName: 'Fred',
        lastName: 'Flintstone'
      }
    }).then(function (response) {
      console.log(response);
    }).catch(function (error) {
      console.log(error);
    });

2. How to perform page Jump after the user creates successfully (the implementation of jump page code is later).

For the implementation of the page jump process, here we use Jump between components and pages and pass parameters in aliice As mentioned in, with router is implemented.

After the completion of the page implementation, the following display will be displayed. After the completion of the subsequent jump page, complete integration test will be carried out.

User list and management

User list rendering

After the user creation is completed, the page will jump to the list page of all users in the display database, which is implemented with the following components:

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 {
  // Page Jump static configuration
  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = {
      userList: [],
    };
  }

  componentDidMount() {
    this.getUserList();
  }

  // Get user list data
  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);
    }
  }

  // Details and update page
  handleClickDetail = (id) => {
    console.log("ListTable id: " + id);

    // Page skipping
    const { history } = this.props;
    history.push({
      pathname: '/user/detail',
      state: { id },
    });
  };

  // Delete data
  handleClickDelete = (id) => {
    this.deleteUser(id);
  };

  // delete user
  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;

For the above codes, the key points to be concerned are as follows:

1. How to use GET request of axios to operate data?

When using Axios for GET requests, it is similar to POST requests in the following two ways. The first is to directly use the encapsulated axios.get for requests. The format is as follows:

// 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: { // Here, the parameters are set as URL parameters (parameters are carried according to the 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);
  }
}

The second is to send a GET request using axios(config {...}):

axios({
  method: 'get',
  url: 'http://bit.ly/2mTM3nY',
  params: {
    id: id,
  }
})
  .then(function (response) {
    console.log(response);
  });

2. How to transfer the required parameters during page Jump?

To transfer parameters during page Jump, you need to set the following in the history.push method:

    // Page skipping
    const { history } = this.props;
    history.push({
      pathname: '/user/detail',
      state: { id },
    });

Then, on the user details page, you can obtain the id parameter passed as follows:

  componentDidMount() {
    let id = this.props.location.state.id;
    console.log("DetailForm id: " + id);
  }

3. How to render the user's entire list page according to userList?

When rendering the entire user list circularly, you need to initialize the entire state of userList as an array in the constructor:

  constructor(props) {
    super(props);
    this.state = {
      userList: [],
    };
  }

Then use the map method in the render method to traverse the array and render the data in the list:

          <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>

After completion, the page is displayed as follows:

For each user, there are details and delete buttons. The details will jump to the user detail page to display the user's specific information, and the delete button will delete the user's information. Let's first look at the implementation of a simple delete method. The implementation of detail is described in the next section.  

User delete implementation

When deleting users in the above list, we also use the axios.get request to delete the user data in the database, then get the new userList return value and update the state using the setState method to make the page render again. The specific code has been given in the above component code, and the implementation is relatively simple.

User details and modifications

The user details page is the page obtained by clicking the detail button for the specified user in the user list page. The implementation of the page component is as follows:

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() {
    // get and post requests using axios
    let id = this.props.location.state.id;
    console.log("DetailForm id: " + id);
    this.getUserByAxios(id);
  }

  // Using axios for get requests
  // Before use, you need to install axios: npm install axios --save and 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);
    }
  }

  // Form change processing function
  handleChange = (event) => {
    const {name, value} = event.target;
    this.setState({
      [name] : value,
    })
  };

  // Update user information function
  handleSubmit = (event) => {
    this.submitUser();
  };

  // post requests to submit updated user information
  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);
        // Update list 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;

In this part of code, axios.get request is used to jump to the incoming id parameter according to the page to get the corresponding details of the user. At the same time, axios.post request is used to process the update operation of user information. In this way, the display of user's detailed information page and the update function of user's detailed information are realized.  

The display of this page is:

 

Ice project routing configuration

In the above code, the following request path is used for page Jump:

/user/list
/user/detail

In the ice project, you need to configure declarative routes in routes.j[t]s route configuration file:

const routes = [
  {
    path: '/user/list',
    component: UserListTable,
  },
  {
    path: '/user/detail',
    component: UserDetailForm,
  },
  ...
];

export default routes;

The above is the full implementation of this instance. END.

315 original articles published, 50 praised and 30 thousand visited+
Private letter follow

Keywords: axios React Spring Fragment

Added by Louis11 on Wed, 04 Mar 2020 08:24:55 +0200