Separation of front-end and back-end Spring Boot + Vue development of single-page application personal summary (1)

Separation of front-end and back-end Spring Boot + Vue development of single-page application personal summary (1)

2018/01/30 Update

    About cross-domain : In the actual development process, it is found that the cross-domain problem is not so easy to solve because the Springboot security control framework uses Securtiy, its authentication is based on JSESSIONID  and the axios framework does not send cookies by default, so it needs to be configured in axios added in

axios.defaults.withCredentials = true

However, due to cross-domain policy issues, the Springboot back-end cross-domain settings also need to be modified

@Configuration
public class CorsConfig {
    /**
     允许任何域名使用
     允许任何头
     允许任何方法(post、get等)
     */
    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // addAllowedOrigin 不能设置为* 因为与 allowCredential 冲突
        corsConfiguration.addAllowedOrigin("http://localhost:9528");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        // allowCredential 需设置为true
        corsConfiguration.setAllowCredentials(true);
        return corsConfiguration;
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig());
        return new CorsFilter(source);
    }
}

foreword

    Requirements: I am learning SpringBoot recently, and I hope I can build a simple Demo application, but I am confused when it comes to the front-end, because most of the front-end tutorials on the Internet are generated by the template engine thymeleaf, which gives me the feeling It is an evolutionary version of JSP, but it is obvious that this development method is already a bit backward. Now the front-end is getting more and more engineering, and frameworks such as Angular/Vue/React are very popular, so I hope to build a front-end application that is more in line with the direction of technological progress (I chose Vue, which is relatively easy to get started with).

    Question: During the process of reviewing the information, I found that there are a lot of introductory materials related to SpringBoot and Vue, but there are relatively few combinations of the two, which led to a lot of pitfalls. After continuous trial and error, I finally successfully built a Demo application and implemented a login instance, so I will summarize and consolidate it here.

    The server side of the project architecture   is based on the SpringBoot framework. In addition to providing forwarding to the home page, it only provides a RESTful interface and interacts with messages in Json format; the front end uses the Vue family bucket as the core to realize SPA single-page application, and communicate with the server side in ajax mode. Communication; the front-end and back-end are separated for development, so two projects will be built, and the project will be packaged (copied into) through npm run build for integration.

    The reader has a certain Java foundation, SpringBoot foundation, and a certain front-end foundation, Vue foundation.

Well, this blog is actually mostly for myself

Introduction to the development environment

  • JDK1.8
  • Node v8.9.3
  • npm v5.5.1
  • Development tool IDEA (install Vue.js plugin)
  • DatabaseMySQL 57
  • Version management tool Git

They are all basic development environments, and the specific construction process is omitted.

Server build

    Use Spring Initializr to create a SpinrgBoot template
image
    with the group name and project name at will. If you add dependencies, it depends on your needs. Here are the core dependencies of the Web and some database dependencies. The core dependencies of the
image
    pom.xml file

    <dependencies>
        <!-- JPA 默认实现为Hibernate -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- 核心依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 可以实现热部署,在IDEA上实现热部署还需一些额外的配置,请查阅资料 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- JDBC for mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- 测试框架 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    Then, add a configuration file, where I use yml to configure, it feels more elegant
    application.xml is placed in the resources root directory, remember to change the database username, password and url to your own

# set server port
server:
  port: 8888  # 配置端口
  context-path: / # 项目启动地址为 localhost:8888/

spring:
  datasource: # set database config
    url: jdbc:mysql://localhost:3306/***?useUnicode=true&characterEncoding=utf8&useSSL=false
    username: *****
    password: *****
    driver-class-name: com.mysql.jdbc.Driver
  jpa: # set jpa
    database: MYSQL # specify ths DBMS
    show-sql: true # show or not log for each sql query
    hibernate:
      ddl-auto: update # Hibernate ddl auto(create, create-drop, update)
      naming: # naming strategy
        strategy: org.hibernate.cfg.ImprovedNamingStrategy
    properties:
      hibernate: # stripped before adding them to entity manager
        dialect: org.hibernate.dialect.MySQL5Dialect
  aop: #设置aop,aop依赖添加后默认是启用的
    proxy-target-class: true

    Create a directory structure only, the directory structure refers to cases on the Internet and your own habits for reference
image

 

    The Demo project only implements the login instance, so the data entity only needs one User, and the User class needs to be annotated with @Entity , which means that this is an entity class, which is managed by Hibernate; at the same time, I use one of the core dependencies of spring boot. validation is used as a parameter validation framework, and the validation method will be implemented in the controller; for the detailed code, please refer to entity/SysUser. If the full code is posted, the space will be very long, so please go to github to see the source code for the detailed code, all with detailed comments

github_spring-vue-demo

    By the way, implement the Dao layer interface of SysUser

/**
 * 用户Dao层
 * 继承JapRepository,可以实现一些默认方法,如save/findAll/findOne/delete/count/exists 等
 * Created by bekey on 2017/12/9.
 */
public interface SysUserRepository extends JpaRepository<SysUser,Integer> {
    //...
}

    We only add a findFirstByNameAndPassword method in SysUserRepository. For detailed source code, see repository/SysUserRepository

    Next, build the service layer. The service layer follows the service interface + implementation model. We have not created users yet, so we will provide saveUser and checkLogin services. For detailed codes, please refer to service/SysUserService and service/SysUserServiceImpl

    The implementation of the service layer is relatively simple and rude, and more functions such as password encryption can be added by modifying the implementation class.

    Then, it's time to build the control layer. Add @RestController to the control class to realize that all methods in this class will automatically return data in Json format!

/**
 *  用户控制层
 * . @RestController 该类下所有返回值默认以json格式进行返回  
 * . @RequestMapping 匹配url地址 /user  
 * . @Validated 代表该类启用参数验证,通过添加注解可以验证参数
 * Created by bekey on 2017/12/20.
 */
@RestController
@RequestMapping("/user")
@Validated
public class SysUserController {
    //...
}

    Now we are not in a hurry to implement the control layer, because we must first agree on the format of the front-end and back-end interaction. The following is a simple format. The code is the status code of 200, which means normal. The message is the message. Usually it should be a simple sentence, and the data is extra. The format of the content message and the generation of a lot of code referencing the github portal , I would like to express my thanks here

{
    "code":200,
    "message":"附带的消息",
    "data":{}
}

    To this end, create class RestResult and enum ResultCode under entity to agree on message format and status code, because RestResult is very common, but it is more troublesome to set up, in order to prevent error return, it is recommended to use factory mode to generate, so add a generation class ResultGenerator under utils For related codes, see entity/RestResult entity/ResultCode utils/ResultGenerator

    Well, after all these are set up, we can finally write a controller/(ㄒoㄒ)/~~ java is long-winded and well-deserved
. Now we only provide two interfaces, register/login, but we need to add parameter validation.

     /**
     * 匹配 /user/register 地址
     * .在实体前添加 @Valid 注解代表要对这个实体进行验证,如果验证不通过就会报异常
     * bindingResult是对异常信息的包装,不过这里我们不用,而是交由异常处理器进行处理
     * @return 注册成功会将注册信息返回(!因为是demo所以没有考虑安全性)
     */
    @RequestMapping("/register")
    public RestResult register(@Valid SysUser user, BindingResult bindingResult) {
        return generator.getSuccessResult("用户注册成功",userService.saveUser(user));
    }

    /**
     * 匹配 /user/login 地址 ,限定POST方法
     * 。@NotNull 在字段前添加注解代表验证该字段,如果为空则报异常
     * @return 登陆成功则返回相关信息,否则返回错误提示
     */
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    public RestResult login(@NotNull(message = "用户名不能为空") String name,@NotNull(message = "密码不能为空") String password, HttpSession session) {
        SysUser user = userService.checkLogin(name, password);
        if(user != null) {
                //储存到session中
            session.setAttribute("user",user);
            return generator.getSuccessResult("登陆成功",user);
        }
        return generator.getFailResult("用户名/密码错误");
    }

    In this way, our interface is written, but what if the parameters fail to pass the verification? It is obviously impossible for the program to report an exception but the user does not get feedback, so we add an exceptionHandler

        /**
     * 为参数验证添加异常处理器
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public RestResult handleConstraintViolationException(ConstraintViolationException cve) {
        //这里简化处理了,cve.getConstraintViolations 会得到所有错误信息的迭代,可以酌情处理
        String errorMessage = cve.getConstraintViolations().iterator().next().getMessage();
        return generator.getFailResult(errorMessage);
    }

    For full version code, please refer to controller/SysUserController

    Run the project and visit localhost:8888 ... Well 404 We don't have a home page, then create an index.html under resources/static

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这是主页</title>
</head>
<body>
    <h1>这是主页</h1>
</body>
</html>

    ok In the default configuration, the framework will automatically find index.html under static as the home page access

http://localhost:8888/user/register?name=myadmin&password=123456

    See if it returns a json to tell you that the registration was successful?

{"code":200,"message":"用户注册成功","data":{"id":1,"name":"myadmin","password":"123456"}}

    OK, then visit again

http://localhost:8888/user/register?name=myadmin&password=1234

    See if it returns a json telling you that the password should be set to 6 to 18 digits?

{"code":400,"message":"密码应设为6至18位","data":null}

    Again

http://localhost:8888/user/register?name=myadmin&password=456789

    See if it returns a json telling you that the primary key/unique constraint is violated?

    try to log in

http://localhost:8888/user/login?name=myadmin&password=123456

    It should be a 404, because only post requests are accepted. If you want to verify, you can
configure it through other methods. Because the front and back ends are separated, for the convenience of development, we need to configure cross-domain. If you don’t understand the cross-domain problem, you can go to check it. Create a new CorsConfig under config with the following information

/**
 * 设置允许跨域
 */
@Configuration
public class CorsConfig {
    /**
     允许任何域名使用  
     允许任何头  
     允许任何方法(post、get等)
     */
    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*"); // 1
        corsConfiguration.addAllowedHeader("*"); // 2
        corsConfiguration.addAllowedMethod("*"); // 3
        return corsConfiguration;
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig()); 
        return new CorsFilter(source);
    }
}

    Most of the work on the server side is basically completed. At present, we have only created two interfaces and a temporary home page, which can be continued after the front-end project is completed.

    Complete source code -> github_spring-vue-demo

Front-end project construction

    Subsequent content Separation of front and back ends Spring Boot + Vue development of single page application personal summary (2)

Project display and source code

A new branch dev has been created on the server-side code Github, which integrates the authentication framework Security, and customizes the Filter to realize dynamic permission control. I will share my personal experience when I have time.

It may be improved in the future, thank you.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325474600&siteId=291194637