<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          動(dòng)手實(shí)踐!從零開始實(shí)現(xiàn)Springboot+Vue登錄

          共 14733字,需瀏覽 30分鐘

           ·

          2020-11-21 22:40


          作者:Eli Shaw

          https://blog.csdn.net/xiaojinlai123/article/details/90694372

          一、簡述

          最近學(xué)習(xí)使用 Vue 實(shí)現(xiàn)前端后端分離,在 Github 上有一個(gè)很好的開源項(xiàng)目:mall,正所謂百看不如一練,自己動(dòng)手實(shí)現(xiàn)了一個(gè) Springboot+Vue 的登錄操作,在此記錄一下踩過的坑。

          文章最后補(bǔ)充兩端的 GitHub 代碼,之所以放在最后,是因?yàn)槲恼聦懙暮芗?xì)致了,動(dòng)手操作一下會(huì)更有幫忙,如果有很大出入可以比對原碼,找找問題。

          二、開發(fā)工具

          VSCode

          IDEA

          Vue 的安裝就不說了,有很多文章,但是 Springboot+Vue 整合的完整文章相對較少,所以我主要記錄一下這兩端整合時(shí)的內(nèi)容。

          (Vue 安裝后就會(huì)有 npm?或 cnpm,相應(yīng)的介紹也不說了,Vue 官網(wǎng)可查看)

          一、打開 cmd 創(chuàng)建 Vue 項(xiàng)目,并添加 Vue 依賴的框架:

          1. 創(chuàng)建 Vue?項(xiàng)目 (進(jìn)入自己想創(chuàng)建的文件夾位置,我放在 D:\VSCodeWorkSpace),創(chuàng)建語句 vue create vue-spring-login-summed,方向鍵選擇創(chuàng)建方式,我選擇的默認(rèn)

          2. 進(jìn)入到創(chuàng)建的 Vue 項(xiàng)目目錄,添加依賴框架:

          cd vue-spring-login-summed (進(jìn)入到項(xiàng)目根目錄)
          vue add element (添加 element,一個(gè) element 風(fēng)格的 UI 框架)
          npm install axios (安裝 axios,用于網(wǎng)絡(luò)請求)
          npm install vuex --save(安裝 Vuex,用于管理狀態(tài))
          npm install vue-router (安裝 路由,用于實(shí)現(xiàn)兩個(gè) Vue 頁面的跳轉(zhuǎn))

          以上命令截圖如下:

          1) 添加 Element

          2) 添加 axios

          3) 添加 Vuex

          4) 添加 路由

          到此相關(guān)依賴的架包添加完畢,輸入 code .?打開?VSCode

          二、添加目錄結(jié)構(gòu)

          在?VSCode?下看到?Vue?整體項(xiàng)目結(jié)構(gòu)如下

          現(xiàn)在需要?jiǎng)?chuàng)建相應(yīng)功能的目錄結(jié)構(gòu),進(jìn)行分層開發(fā),需要在?src?目錄下創(chuàng)建下面幾個(gè)目錄

          api (網(wǎng)絡(luò)請求接口包)
          router (路由配置包)
          store (Vuex 狀態(tài)管理包)
          utils (工具包)
          views (vue 視圖包,存放所有 vue 代碼,可根據(jù)功能模塊進(jìn)行相應(yīng)分包)

          創(chuàng)建后的目錄結(jié)構(gòu)如下

          三、運(yùn)行項(xiàng)目

          現(xiàn)在可以運(yùn)行項(xiàng)目了,在 VSCode 菜單欄依次選擇:終端 ——?運(yùn)行任務(wù)...

          這里使用的是 serve?模式,即開發(fā)模式運(yùn)行的項(xiàng)目

          在瀏覽器輸入:http://localhost:8080/

          這是 Vue 默認(rèn)的頁面,代表項(xiàng)目創(chuàng)建成功了,在進(jìn)行代碼開發(fā)前,先貼上項(xiàng)目整體結(jié)構(gòu),防止不知道在哪創(chuàng)建

          四、View?層代碼編寫

          編寫三個(gè) vue 文件:login.vue(登錄頁面)、success.vue(登錄成功頁面)、error.vue(登錄失敗頁面)

          1.login.vue

          代碼如下 (比較懶,直接從?mall?扒下來的代碼,去掉了一些功能)






          2.success.vue



          3.error.vue



          五、路由

          頁面寫好了,我們需要依次顯示這三個(gè)頁面,這里我們統(tǒng)一使用路由來管理顯示頁面,路由的官方文檔見:vue 路由

          本著先實(shí)踐,后理解的碼農(nóng)學(xué)習(xí)方式。我們先使用路由顯示三個(gè)頁面后,再去理解 Vue 路由這個(gè)功能點(diǎn)。

          1. 創(chuàng)建路由配置文件

          在剛才建立的 router?文件夾下創(chuàng)建一個(gè) index.js?文件,內(nèi)容如下

          import Vue from 'vue' //引入 Vue
          import VueRouter from 'vue-router' //引入 Vue 路由

          Vue.use(VueRouter); //安裝插件

          export const constantRouterMap = \[
          //配置默認(rèn)的路徑,默認(rèn)顯示登錄頁
          { path: '/', component: () => import('@/views/login')},

          //配置登錄成功頁面,使用時(shí)需要使用 path 路徑來實(shí)現(xiàn)跳轉(zhuǎn)
          { path: '/success', component: () => import('@/views/success')},

          //配置登錄失敗頁面,使用時(shí)需要使用 path 路徑來實(shí)現(xiàn)跳轉(zhuǎn)
          { path: '/error', component: () => import('@/views/error'), hidden: true }
          \]

          export default new VueRouter({
          // mode: 'history', //后端支持可開
          scrollBehavior: ()
          => ({ y: 0 }),
          routes: constantRouterMap //指定路由列表
          })

          2. 將路由添加到程序入口

          路由配置文件寫好,我們需要把他引入到 main.js 中,在項(xiàng)目的 src 目錄根節(jié)點(diǎn)下,找到 main.js,添加內(nèi)容如下:

          import Vue from 'vue'
          import App from './App.vue'
          import './plugins/element.js'
          import router from './router' //引入路由配置

          Vue.config.productionTip = false

          new Vue({
          render: h => h(App)
          ,
          router, //使用路由配置
          }).$mount('#app')

          3. 配置路由的出入口

          現(xiàn)在路由已經(jīng)完全引入到項(xiàng)目了,但是路由還需要一個(gè)出入口,這個(gè)出入口用來告訴路由將路由的內(nèi)容顯示在這里。上面 main.js 配置的第一個(gè) vue 顯示頁面為 App.vue ,因此我們修改 App.vue 內(nèi)容如下




          ?就是顯示路由的出入口。

          現(xiàn)在保存 App.vue 文件后,當(dāng)前項(xiàng)目會(huì)被重新裝載運(yùn)行,在剛才瀏覽的界面就會(huì)看到登錄界面如下:

          4. 路由跳轉(zhuǎn)

          在 login.vue?中可以使用 this.$router.push({path: "路徑"})?來跳轉(zhuǎn)到指定路徑的路由組件中,下面是通過路由跳轉(zhuǎn)到 error.vue?與 success.vue 的代碼

          this.$router.push({path: "/success"}); //跳轉(zhuǎn)到成功頁

          this.$router.push({path: "/error"}); //跳轉(zhuǎn)到失敗頁

          六、使用?Vuex + Axios?方式進(jìn)行網(wǎng)絡(luò)請求

          1.Axios

          axios 是一個(gè)網(wǎng)絡(luò)請求構(gòu)架,官方推薦使用這種方式進(jìn)行 http 的請求。

          1)?在 utils?包下封裝一個(gè)請求工具類 request.js

          import axios from 'axios' //引入 axios
          import baseUrl from '../api/baseUrl' //使用環(huán)境變量 + 模式的方式定義基礎(chǔ)URL

          // 創(chuàng)建 axios 實(shí)例
          const service = axios.create({
          baseURL: baseUrl, // api 的 base\_url
          timeout: 15000, // 請求超時(shí)時(shí)間
          })

          export default service

          這里的 baseUrl 涉及 Vue CLI3 的環(huán)境變量與模式的概念,見:Vue 環(huán)境變量和模式 (設(shè)置通用 baseUrl)

          2)?登錄請求接口 API

          在 api 文件夾下,創(chuàng)建一個(gè)登錄 API 文件:login.js

          import request from '@/utils/request' //引入封裝好的 axios 請求

          export function login(username, password) { //登錄接口
          return request({ //使用封裝好的 axios 進(jìn)行網(wǎng)絡(luò)請求
          url: '/admin/login',
          method: 'post',
          data: { //提交的數(shù)據(jù)
          username,
          password
          }
          })
          }

          2. 使用 Vuex?封裝 axios

          Vuex 是一個(gè)狀態(tài)管理構(gòu)架,官方文檔:Vuex

          1) 封裝 Vuex 中的?module

          在 store?文件夾下創(chuàng)建一個(gè) modules?文件夾,然后在此文件夾下創(chuàng)建一個(gè) user.js?文件

          import { login } from '@/api/login'//引入登錄 api 接口

          const user = {
          actions: {
          // 登錄
          Login({ commit }, userInfo) { //定義 Login 方法,在組件中使用 this.$store.dispatch("Login") 調(diào)用
          const username = userInfo.username.trim()
          return new Promise((resolve, reject) => { //封裝一個(gè) Promise
          login(username, userInfo.password).then(response => { //使用 login 接口進(jìn)行網(wǎng)絡(luò)請求
          commit('') //提交一個(gè) mutation,通知狀態(tài)改變
          resolve(response) //將結(jié)果封裝進(jìn) Promise
          }).catch(error => {
          reject(error)
          })
          })
          },
          }
          }
          export default user

          這里的代碼值得解釋一下:官方文檔對應(yīng):Vuex actions

          1. 首先引入 login 接口,之后使用登錄接口進(jìn)行網(wǎng)絡(luò)請求。

          2. 定義一個(gè)?名為 Login?的 action?方法,Vue 組件通過 this.$store.dispatch("Login") 調(diào)用

          3.Promise,這個(gè)類很有意思,官方的解釋是 “store.dispatch?可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且?store.dispatch?仍舊返回 Promise”。這話的意思組件中的 dispatch 返回的仍是一個(gè) Promise 類,因此推測 Promise 中的兩個(gè)方法 resolve() 與 reject() 分別對應(yīng) dispatch 中的 then 與 catch。

          2) 創(chuàng)建 Vuex

          在 store?文件夾下創(chuàng)建一個(gè) index.js?文件

          import Vue from 'vue' //引入 Vue
          import Vuex from 'vuex' //引入 Vuex
          import user from './modules/user' //引入 user module

          Vue.use(Vuex)

          const store = new Vuex.Store({
          modules: {
          user //使用 user.js 中的 action
          }
          })

          export default store

          3)?將?Vuex?添加到 main.js?文件

          修改之前的 main.js 文件如下:

          import Vue from 'vue'
          import App from './App.vue'
          import './plugins/element.js'
          import router from './router' //引入路由配置
          import store from './store' //引入 Vuex 狀態(tài)管理

          Vue.config.productionTip = false

          new Vue({
          render: h => h(App)
          ,
          router, //使用路由配置
          store //使用 Vuex 進(jìn)行狀態(tài)管理
          }).$mount('#app')

          重新運(yùn)行項(xiàng)目,在 Chrome 瀏覽器中進(jìn)入調(diào)試模式,點(diǎn)擊登錄按鈕

          可以看到有發(fā)送一個(gè) 8088 端口的請求,至此 Vue 端的所有代碼已經(jīng)完成。

          -------------------------------Springboot?開發(fā) -------------------------------

          項(xiàng)目創(chuàng)建就不提了,網(wǎng)上有很多,只要使用 Spring Assistant 創(chuàng)建就好。

          整體目錄結(jié)構(gòu)如下

          1. 在 application.yml 修改端口號(hào)

          不要和 Vue 在一個(gè) 8080 端口上:

          server:
          port: 8088

          2. 解決跨域問題

          這里有一個(gè)跨域問題,即 Vue 使用 8080?端口,要訪問 8088 端口的服務(wù)器,會(huì)報(bào)錯(cuò)。錯(cuò)誤信息如下:

          Access to XMLHttpRequest at 'http://localhost:8088/admin/login' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No'Access-Control-Allow-Origin' header is present on the requested resource.

          這個(gè)問題在 Vue 端或在 Springboot 端處理都可以,我在 Springboot 端處理的,寫一個(gè) CorsConfig 類內(nèi)容如下,不要忘了?@Configuration 注解。

          @Configuration
          public class CorsConfig {
          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()); // 4
          return new CorsFilter(source);
          }
          }

          3.IErrorCode?接口

          Java 版本

          public interface IErrorCode {
          long getCode();
          String getMessage();
          }

          Kotlin 版本

          interface IErrorCode {
          fun getCode(): Long
          fun getMessage(): String
          }

          4.CommonResult 類

          Java?版本

          public class CommonResult<T> {
          private long code;
          private String message;
          private T data;

          protected CommonResult() {
          }

          protected CommonResult(long code, String message, T data) {
          this.code = code;
          this.message = message;
          this.data = data;
          }

          /\*\*
          \* 成功返回結(jié)果
          \*
          \* @param data 獲取的數(shù)據(jù)
          \*/
          public static CommonResult success(T data) {
          return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
          }

          /\*\*
          \* 成功返回結(jié)果
          \*
          \* @param data 獲取的數(shù)據(jù)
          \* @param message 提示信息
          \*/
          public static CommonResult success(T data, String message) {
          return new CommonResult(ResultCode.SUCCESS.getCode(), message, data);
          }

          /\*\*
          \* 失敗返回結(jié)果
          \*
          \* @param errorCode 錯(cuò)誤碼
          \*/
          public static CommonResult failed(IErrorCode errorCode) {
          return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null);
          }

          /\*\*
          \* 失敗返回結(jié)果
          \*
          \* @param message 提示信息
          \*/
          public static CommonResult failed(String message) {
          return new CommonResult(ResultCode.FAILED.getCode(), message, null);
          }

          /\*\*
          \* 失敗返回結(jié)果
          \*/
          public static CommonResult failed() {
          return failed(ResultCode.FAILED);
          }

          /\*\*
          \* 參數(shù)驗(yàn)證失敗返回結(jié)果
          \*/
          public static CommonResult validateFailed() {
          return failed(ResultCode.VALIDATE\_FAILED);
          }

          /\*\*
          \* 參數(shù)驗(yàn)證失敗返回結(jié)果
          \*
          \* @param message 提示信息
          \*/
          public static CommonResult validateFailed(String message) {
          return new CommonResult(ResultCode.VALIDATE\_FAILED.getCode(), message, null);
          }

          /\*\*
          \* 未登錄返回結(jié)果
          \*/
          public static CommonResult unauthorized(T data) {
          return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
          }

          /\*\*
          \* 未授權(quán)返回結(jié)果
          \*/
          public static CommonResult forbidden(T data) {
          return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
          }

          public long getCode() {
          return code;
          }

          public void setCode(long code) {
          this.code = code;
          }

          public String getMessage() {
          return message;
          }

          public void setMessage(String message) {
          this.message = message;
          }

          public T getData() {
          return data;
          }

          public void setData(T data) {
          this.data = data;
          }
          }

          Kotlin?版本

          class CommonResult<T> {
          var code: Long = 0
          var message: String? = null
          var data: T? = null

          constructor(code: Long, message: String, data: T?) {
          this.code = code
          this.message = message
          this.data = data
          }

          companion object {

          /\*\*
          \* 成功返回結(jié)果
          \* @param data 獲取的數(shù)據(jù)
          \*/
          fun success(data: T): CommonResult {
          return CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data)
          }

          /\*\*
          \* 成功返回結(jié)果
          \* @param data 獲取的數(shù)據(jù)
          \* @param message 提示信息
          \*/
          fun success(data: T, message: String): CommonResult {
          return CommonResult(ResultCode.SUCCESS.getCode(), message, data)
          }

          /\*\*
          \* 失敗返回結(jié)果
          \* @param errorCode 錯(cuò)誤碼
          \*/
          fun failed(errorCode: IErrorCode): CommonResult {
          return CommonResult(errorCode.getCode(), errorCode.getMessage(), null)
          }

          /\*\*
          \* 失敗返回結(jié)果
          \* @param message 提示信息
          \*/
          fun failed(message: String): CommonResult {
          return CommonResult(ResultCode.FAILED.getCode(), message, null)
          }

          /\*\*
          \* 失敗返回結(jié)果
          \*/
          fun failed(): CommonResult {
          return failed(ResultCode.FAILED)
          }

          /\*\*
          \* 參數(shù)驗(yàn)證失敗返回結(jié)果
          \*/
          fun validateFailed(): CommonResult {
          return failed(ResultCode.VALIDATE\_FAILED)
          }

          /\*\*
          \* 參數(shù)驗(yàn)證失敗返回結(jié)果
          \* @param message 提示信息
          \*/
          fun validateFailed(message: String): CommonResult {
          return CommonResult(ResultCode.VALIDATE\_FAILED.getCode(), message, null)
          }

          /\*\*
          \* 未登錄返回結(jié)果
          \*/
          fun unauthorized(data: T): CommonResult {
          return CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data)
          }

          /\*\*
          \* 未授權(quán)返回結(jié)果
          \*/
          fun forbidden(data: T): CommonResult {
          return CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data)
          }
          }
          }

          5.ResultCode?枚舉

          Java?版本

          public enum ResultCode implements IErrorCode {
          SUCCESS(200, "操作成功"),
          FAILED(500, "操作失敗"),
          VALIDATE\_FAILED(404, "參數(shù)檢驗(yàn)失敗"),
          UNAUTHORIZED(401, "暫未登錄或token已經(jīng)過期"),
          FORBIDDEN(403, "沒有相關(guān)權(quán)限");
          private long code;
          private String message;

          private ResultCode(long code, String message) {
          this.code = code;
          this.message = message;
          }

          public long getCode() {
          return code;
          }

          public String getMessage() {
          return message;
          }
          }

          Kotlin?版本

          enum class ResultCode(private val code: Long, private val message: String) : IErrorCode {
          SUCCESS(200, "操作成功"),
          FAILED(500, "操作失敗"),
          VALIDATE\_FAILED(404, "參數(shù)檢驗(yàn)失敗"),
          UNAUTHORIZED(401, "暫未登錄或token已經(jīng)過期"),
          FORBIDDEN(403, "沒有相關(guān)權(quán)限");

          override fun getCode(): Long {
          return code
          }

          override fun getMessage(): String {
          return message
          }
          }

          6.User 類

          Java?版本

          public class User {

          private int id;
          private String username;
          private String password;

          public int getId() {
          return id;
          }

          public void setId(int id) {
          this.id = id;
          }

          public String getUsername() {
          return username;
          }

          public void setUsername(String username) {
          this.username = username;
          }

          public String getPassword() {
          return password;
          }

          public void setPassword(String password) {
          this.password = password;
          }
          }

          Kotlin?版本

          data class User(
          val id: Int,
          val username: String,
          val password: String)

          7.LoginController?類

          Java?版本

          @RestController
          public class LoginController {

          @RequestMapping(value = "/admin/login", method = RequestMethod.POST)
          public CommonResult login(@RequestBody User user) {
          if (user.getUsername().equals("admin") && user.getPassword().equals("123456"))
          return CommonResult.success("admin");
          else
          return CommonResult.validateFailed();
          }
          }

          Kotlin?版本

          @RestController //此注解是 @ResponseBody 和 @Controller 的組合注解,可返回一個(gè) JSON
          class LoginController {

          @RequestMapping(value = \["/admin/login"\], method = \[RequestMethod.POST\])
          fun admin(@RequestBody user: User): CommonResult<\*> {
          return if (user.username == "admin" && user.password == "123456") {
          CommonResult.success("admin")
          } else {
          CommonResult.validateFailed()
          }
          }
          }

          啟動(dòng)兩端程序

          輸入正確的賬號(hào)密碼

          輸入錯(cuò)誤的賬號(hào)密碼

          七、GitHub 源碼地址

          vue 端:https://github.com/xiaojinlai/vue-spring-login-summed

          Java 端:https://github.com/xiaojinlai/vue-login-java

          Java 端 - Kotlin 版本:https://github.com/xiaojinlai/vue-login-kotlin

          注:Kotlin 版本只是我本人用習(xí)慣了 Kotlin,就功能而言與 Java 是一樣的。大家如果不喜歡可以不用理會(huì),如果有感興趣的可以看看,Kotlin 是 Google 推出的一種簡潔性語言,主推在 Android 上,用習(xí)慣后還是蠻喜歡的。學(xué)習(xí)起來也不難,內(nèi)容也不多,推薦一個(gè)學(xué)習(xí) Kotlin 的網(wǎng)址:https://www.kotlincn.net/docs/reference/


          更多精彩推薦

          ??外包程序員入職螞蟻金服被質(zhì)疑,網(wǎng)友:人生污點(diǎn)
          ??前后端分離三連問:為何分離?如何分離?分離后的接口規(guī)范?
          ??如何設(shè)計(jì)一個(gè)通用的權(quán)限管理系統(tǒng)
          ??去一家小公司從0到1搭建后端架構(gòu),做個(gè)總結(jié)!
          ??這應(yīng)該是全網(wǎng)最全的Git分支開發(fā)規(guī)范手冊~

          最后,推薦給大家一個(gè)有趣有料的公眾號(hào):寫代碼的渣渣鵬,7年老程序員教你寫bug,回復(fù) 面試或資源 送一你整套開發(fā)筆記 有驚喜哦

          瀏覽 125
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91蝌蚪91 九色啦 | www.A片| 成人无码一区二区含羞草 | 五月丁香婷婷综合在线 | 亚洲精品成人片在线播放波多野吉 |