先後端分離 Spring Boot + Vue 開發單頁面應用 我的總結(一)

先後端分離 Spring Booot + Vue 開發單頁面應用 我的總結(一)

2018/10/31 更新

仍是跨越,由於年少無知,文章中分享的方案並非最優解,存在不少侷限的地方,所以誤人子弟十分抱歉。對如今而言,我跨域的解決方案分兩種。1、在開發過程當中,vue提供了優雅的解決方式,即經過自身的反向代理實現(實際上我也不太清楚是基於包依賴,仍是webpack或node實現),通常在配置文件配置,在你的項目中找到相似的地方
module.exports = {
    dev: {
	       ....
		   proxyTable: {
		       // 配置反向代理
		   }
		   ....
	   }
}

貼一下個人配置方式javascript

proxyTable: {
   '/api': {  // 代理轉發的地址前綴以api開頭,/ 表明全部請求
     target: 'http://localhost:8080',    // 轉發到
     changeOrigin: true,
     pathRewrite: {  // url重寫
       '^/api': ''    // 將/api前綴去掉
     }
   },
 },

這種配置方式,後端不用作任何配置,即我後面介紹的CorsConfig是徹底不須要的,並且axios的baseurl也是不須要配置的~注意,這種配置只對開發環境即dev環境生效,當npm run build以後,部署的時候實際上並不會生效。 那麼在生產環境,1、若是像我後面介紹的將打包文件直接丟到springboot static下,那是不會產生跨域問題的,原本就是同源的。其次,若是單獨部署,那麼nginx等等都有反向代理功能,只能配置這些服務器的方向代理就ok了~html

2018/01/30 更新

    關於跨域_:_在實際開發過程當中,發現跨域問題並非那麼好解決的 由於Springboot安全控制框架使用了Securtiy,它的身份認證基於 JSESSIONID 而axios框架默認是不發送cookie的,所以須要在axios配置中添加前端

axios.defaults.withCredentials = true

然而由於跨域策略問題,Springboot後端跨域設置也要修改

@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);
    }
}

前言

_    需求:_ 最近本人在學習SpringBoot,但願本身能搭一個簡單的Demo應用出來,可是搭到前端的時候遇到了困惑,由於網絡上大部分教程前端都是應用模板引擎thymeleaf生成的,它給個人感受就是一個進化版的JSP,可是很明顯這種開發方式已經有些落後了。如今前端愈來愈工程化,Angular/Vue/React等框架很是流行,因此我但願搭建一個更符合技術進步方向的前端應用(我選擇了相對容易入門的Vue)。vue

_    問題:_ 在查閱資料過程,我發現SpringBoot和Vue相關的入門材料很是的多,可是關於二者結合的相對較少,致使踩了很多坑。在不斷得試錯之下,終於成功搭建了一個Demo應用,並實現一個登錄實例,所以在此總結鞏固。java

_    項目架構_  服務端以SpringBoot框架爲核心,除提供轉發到首頁外,只提供RESTful接口,經過Json格式消息進行交互;前端以Vue全家桶爲核心,實現SPA單頁面應用,以ajax方式與服務端進行通訊;先後端分離開發,所以會建兩個項目,經過npm run build 打包項目(複製進)項目進行整合。node

**    閱讀者有必定的Java基礎,SpringBoot基礎,同時有必定的前端基礎,Vue基礎。**mysql

好吧,這篇博客其實主要是寫給我本身的webpack

開發環境介紹

  • JDK1.8
  • Node v8.9.3
  • npm v5.5.1
  • 開發工具IDEA(安裝Vue.js插件)
  • 數據庫MySQL 57
  • 版本管理工具 Git

都是一些基礎的開發環境,具體搭建過程略。ios

服務端搭建

    利用Spring Initializr 建立一個SpinrgBoot模板
image
    組名及項目名隨意,添加依賴的話視需求而定,這裏就添加了Web的核心依賴和一些數據庫的依賴
image
    pom.xml 文件 核心依賴nginx

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

    而後,添加配置文件,在這裏我用yml進行配置,感受比較優雅
    application.xml 放置在resources根目錄下 記得把數據庫username和password和url改成本身的

# 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

    建立目錄僅結構,目錄結構參考了網絡上的案例和本身的習慣,供參考
image

    Demo項目僅實現登錄實例,所以數據實體只須要一個User就好,User類上需加 @Entity 註解,表明這是實體類,交由Hibernate進行管理;同時,我使用了spring boot核心依賴之一的validation做爲參數驗證框架,驗證方法會在controller中實現;詳細代碼參閱 entity/SysUser 若是全貼代碼的話,篇幅會很是的長,因此詳細代碼請到github上看源碼,都帶有詳細的註釋

github_spring-vue-demo

    順便把SysUser的Dao層接口實現

/**
 * 用戶Dao層
 * 繼承JapRepository,能夠實現一些默認方法,如save/findAll/findOne/delete/count/exists 等
 * Created by bekey on 2017/12/9.
 */
public interface SysUserRepository extends JpaRepository<SysUser,Integer> {
    //...
}

    咱們在SysUserRepository中只添加一個findFirstByNameAndPassword方法就夠了,詳細源碼參見 repository/SysUserRepository

    接着,搭建服務層,服務層遵循服務接口 + 實現 的模式,咱們如今尚未建立用戶,那麼就提供 saveUser 和 checkLogin 兩個服務好啦,詳細代碼參閱 service/SysUserService 和 service/SysUserServiceImpl

    服務層實現方式都比較簡單粗暴,經過修改實現類能夠增長密碼加密等更多功能

    而後,該搭建控制層了,爲控制類添加 @RestController 就能夠實現該類下全部方法都會自動以Json格式返回數據啦!

/**
 *  用戶控制層
 * . @RestController 該類下全部返回值默認以json格式進行返回  
 * . @RequestMapping 匹配url地址 /user  
 * . @Validated 表明該類啓用參數驗證,經過添加註解能夠驗證參數
 * Created by bekey on 2017/12/20.
 */
@RestController
@RequestMapping("/user")
@Validated
public class SysUserController {
    //...
}

    如今還不急着實現控制層,由於咱們先要約定先後端交互的格式,下面是一個簡易的格式 ,code是狀態碼200表明正常,message是消息一般應該是簡單的一句話,data是額外的內容
消息格式及生成大量參考了 github傳送門 的代碼,在此表示感謝

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

    爲此,在entity下建立class RestResult 和 enum ResultCode 約定消息格式及狀態碼,由於RestResult十分經常使用,設置卻比較麻煩,爲防止錯誤返回,建議採用工廠模式生成,因此要在utils下添加一個生成類ResultGenerator 相關代碼參閱 entity/RestResult  entity/ResultCode  utils/ResultGenerator

    好啦,這些搭完終於能夠寫controller了 /(ㄒoㄒ)/~~ java囉嗦果真名不虛傳
如今咱們只提供兩個接口 分別是 register/login ,可是要添加參數驗證

/**
     * 匹配 /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("用戶名/密碼錯誤");
    }

    這樣咱們的接口就寫好了,可是若是參數沒經過驗證怎麼辦呢?程序報異常可是用戶卻得不到反饋是明顯不能夠的,因此咱們添加一個exceptionHandler

/**
     * 爲參數驗證添加異常處理器
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public RestResult handleConstraintViolationException(ConstraintViolationException cve) {
        //這裏簡化處理了,cve.getConstraintViolations 會獲得全部錯誤信息的迭代,能夠酌情處理
        String errorMessage = cve.getConstraintViolations().iterator().next().getMessage();
        return generator.getFailResult(errorMessage);
    }

    完整版代碼請參閱 controller/SysUserController

    項目跑一下,訪問localhost:8888 ... 嗯 404 咱們沒有主頁,那在resources/static 下建立一個index.html,將就一下

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

    ok 在缺省配置下,框架會自動找到static下index.html 做爲主頁的 訪問

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

    看是否是返回一個json,告訴你註冊成功了呢?

{"code":200,"message":"用戶註冊成功","data":{"id":1,"name":"myadmin","password":"123456"}}

    好,那再訪問一次

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

    看是否是返回一個json,告訴你密碼應設爲6至18位了呢?

{"code":400,"message":"密碼應設爲6至18位","data":null}

    再來一次

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

    看是否是返回一個json,告訴你違反主鍵/惟一約束條件了呢?

    試着登錄一下

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

    應該是一個404,由於只接受post請求,若是要驗證能夠經過其它方法
配置尚未結束 由於先後端分離,爲了開發的方便,咱們須要配置一下跨域,關於跨域問題,不理解的話能夠去查閱一下資料 在config下新建一個CorsConfig

/**
 * 設置容許跨域
 */
@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);
    }
}

    服務端的工做基本就完成大部分了,目前咱們僅建立了兩個接口,和一個臨時主頁,稍後完成前端項目以後才能繼續。

    完總體源碼 -> github_spring-vue-demo

前端項目搭建

後續內容 先後端分離 Spring Boot + Vue 開發單頁面應用 我的總結(二)

項目展現及源碼

服務端代碼Github上建立了新的分支dev,整合了認證框架Security,並自定義Filter實現動態權限控制,有空的時候會分享一下我的體會

之後可能完善,謝謝。

相關文章
相關標籤/搜索