1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 14733字,需瀏覽 30分鐘

         ·

        2020-11-21 22:40


        作者:Eli Shaw

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

        一、簡述

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

        文章最后補充兩端的 GitHub 代碼,之所以放在最后,是因為文章寫的很細致了,動手操作一下會更有幫忙,如果有很大出入可以比對原碼,找找問題。

        二、開發(fā)工具

        VSCode

        IDEA

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

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

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

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

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

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

        以上命令截圖如下:

        1) 添加 Element

        2) 添加 axios

        3) 添加 Vuex

        4) 添加 路由

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

        二、添加目錄結構

        在?VSCode?下看到?Vue?整體項目結構如下

        現(xiàn)在需要創(chuàng)建相應功能的目錄結構,進行分層開發(fā),需要在?src?目錄下創(chuàng)建下面幾個目錄

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

        創(chuàng)建后的目錄結構如下

        三、運行項目

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

        這里使用的是 serve?模式,即開發(fā)模式運行的項目

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

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

        四、View?層代碼編寫

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

        1.login.vue

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






        2.success.vue



        3.error.vue



        五、路由

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

        本著先實踐,后理解的碼農學習方式。我們先使用路由顯示三個頁面后,再去理解 Vue 路由這個功能點。

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

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

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

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

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

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

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

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

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

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

        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)在路由已經完全引入到項目了,但是路由還需要一個出入口,這個出入口用來告訴路由將路由的內容顯示在這里。上面 main.js 配置的第一個 vue 顯示頁面為 App.vue ,因此我們修改 App.vue 內容如下




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

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

        4. 路由跳轉

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

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

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

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

        1.Axios

        axios 是一個網(wǎng)絡請求構架,官方推薦使用這種方式進行 http 的請求。

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

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

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

        export default service

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

        2)?登錄請求接口 API

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

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

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

        2. 使用 Vuex?封裝 axios

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

        1) 封裝 Vuex 中的?module

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

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

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

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

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

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

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

        2) 創(chuàng)建 Vuex

        在 store?文件夾下創(chuàng)建一個 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 進行狀態(tài)管理
        }).$mount('#app')

        重新運行項目,在 Chrome 瀏覽器中進入調試模式,點擊登錄按鈕

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

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

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

        整體目錄結構如下

        1. 在 application.yml 修改端口號

        不要和 Vue 在一個 8080 端口上:

        server:
        port: 8088

        2. 解決跨域問題

        這里有一個跨域問題,即 Vue 使用 8080?端口,要訪問 8088 端口的服務器,會報錯。錯誤信息如下:

        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.

        這個問題在 Vue 端或在 Springboot 端處理都可以,我在 Springboot 端處理的,寫一個 CorsConfig 類內容如下,不要忘了?@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;
        }

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

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

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

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

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

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

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

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

        /\*\*
        \* 未授權返回結果
        \*/
        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 {

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

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

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

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

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

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

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

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

        /\*\*
        \* 未授權返回結果
        \*/
        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ù)檢驗失敗"),
        UNAUTHORIZED(401, "暫未登錄或token已經過期"),
        FORBIDDEN(403, "沒有相關權限");
        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ù)檢驗失敗"),
        UNAUTHORIZED(401, "暫未登錄或token已經過期"),
        FORBIDDEN(403, "沒有相關權限");

        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 的組合注解,可返回一個 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()
        }
        }
        }

        啟動兩端程序

        輸入正確的賬號密碼

        輸入錯誤的賬號密碼

        七、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 版本只是我本人用習慣了 Kotlin,就功能而言與 Java 是一樣的。大家如果不喜歡可以不用理會,如果有感興趣的可以看看,Kotlin 是 Google 推出的一種簡潔性語言,主推在 Android 上,用習慣后還是蠻喜歡的。學習起來也不難,內容也不多,推薦一個學習 Kotlin 的網(wǎng)址:https://www.kotlincn.net/docs/reference/


        更多精彩推薦

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

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

        瀏覽 125
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            裸体美女一级毛片免费艳福AV | 亚洲AV成人无码精电影在线 | 大香蕉人妻在线 | 北条麻妃影音先锋 | 全部裸体做爰大片免费看网站 | 免费观看成年人视频 | 国产日本欧美视频 | 亚洲黄色无码 | 超碰在线人妻 | 性爱久久|