Laravel+vue.js 前後端分離解決跨域後獲取資料庫使用者列表(五)

Sloth發表於2020-02-06

接續於 = >《Vue.js+Laravel 前後端分離 API 多使用者多表登陸課題(四)》

安裝第三方擴充套件spatie/laravel-cors,花一分鐘時間配置,就可以解決跨域問題。具體簡單的操作可以前往大神的Github閱讀文件。

下面開始講述獲取學生和老師的資料庫的使用者列表,並顯示在前端介面上。首先完善後端Laravel的程式碼,因為學生和老師的程式碼是一樣的,下面以學生的程式碼為例子進行講述。

建立學生資源類

php artisan make:resource StudentResource

對生成的檔案app/Http/Resources/StudentResource.php進行修改

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\Resource;

class StudentResource extends Resource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'created_at' => (string)$this->created_at,
            'updated_at' => (string)$this->updated_at,
        ];
    }
}

建立學生控制器

php artisan make:controller Home/StudentController

對生成的檔案app/Http/Controllers/Home/StudentController.php進行修改

<?php

namespace App\Http\Controllers\Home;

use App\Http\Resources\StudentResource;
use App\Models\Student;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class StudentController extends Controller
{
    public function index()
    {
        $students = Student::all();

        return StudentResource::collection($students);
    }
}

修改路由routes/api.php

<?php

use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::namespace('Home')->group(function () {
    Route::get("student", "StudentController@index");
    Route::get("teacher", "TeacherController@index");
});

建立 database/seeds/StudentsAndTeacherSeeder.php 檔案

php artisan make:seed StudentsAndTeacherSeeder

新增測試資料

<?php

use App\Models\Student;
use App\Models\Teacher;
use Illuminate\Database\Seeder;

class StudentsAndTeacherSeeder extends Seeder {
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run() {
        Student::query()->truncate();
        Student::create([
            "name" => "student1",
            "email" => "student1@gmail.com",
            'password' => bcrypt('student1'),
        ]);
        Student::create([
            "name" => "student2",
            "email" => "student2@gmail.com",
            'password' => bcrypt('student2'),
        ]);
        Student::create([
            "name" => "student3",
            "email" => "student3@gmail.com",
            'password' => bcrypt('student3'),
        ]);
        Student::create([
            "name" => "student4",
            "email" => "student4@gmail.com",
            'password' => bcrypt('student4'),
        ]);
        Student::create([
            "name" => "student5",
            "email" => "student5@gmail.com",
            'password' => bcrypt('student5'),
        ]);

        Teacher::query()->truncate();
        Teacher::create([
            "name" => "teacher1",
            "email" => "teacher1@gmail.com",
            'password' => bcrypt('teacher1'),
        ]);
        Teacher::create([
            "name" => "teacher2",
            "email" => "teacher2@gmail.com",
            'password' => bcrypt('teacher2'),
        ]);
        Teacher::create([
            "name" => "teacher3",
            "email" => "teacher3@gmail.com",
            'password' => bcrypt('teacher3'),
        ]);
        Teacher::create([
            "name" => "teacher4",
            "email" => "teacher4@gmail.com",
            'password' => bcrypt('teacher4'),
        ]);
        Teacher::create([
            "name" => "teacher5",
            "email" => "teacher5@gmail.com",
            'password' => bcrypt('teacher5'),
        ]);
    }
}

匯入資料庫

php artisan db:seed --class=StudentsAndTeacherSeeder

使用Postman測試Api,成功獲取相應的資料

Laravel+vue.js前後端分離解決跨域後獲取資料庫使用者列表(五)

至此後端程式碼開發完畢,接下來講述前端Vue.js的部署


前端部分,新建環境變數檔案.env.local

VUE_APP_API_URL=http://mytestdomain.test/
VUE_APP_AUTH_CLIENT_ID=2
VUE_APP_AUTH_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxx

對axios封裝一個src/utils/http/index.js

import axios  from 'axios' // 基於promise的http庫,攔截請求和響應、取消請求、轉換json、客戶端防禦cSRF等
import { Message } from 'element-ui' // 常用於主動操作後的反饋提示,用於系統級通知的被動提醒。
import router from '../../router' // 引入路由

// 自定義 axios 例項新增攔截器,傳送請求前可以進行一個請求的攔截,檢測是否符合相應的許可權操作
const httpRequest = axios.create({
  timeout: 10000, //設定預設的請求超時時間。例如超過了10s,就會告知使用者當前請求超時,請重新整理等。
  baseURL: process.env.VUE_APP_API_URL //設定請求地址
})

// 新增請求攔截器
httpRequest.interceptors.request.use(
  // 在傳送請求之前做些什麼
  // 每次傳送請求之前判斷vuex中是否存在token        
  // 如果存在,則統一在http請求的header都加上token,這樣後臺根據token判斷你的登入情況
  // 即使本地存在token,也有可能token是過期的,所以在響應攔截器中要對返回狀態進行判斷 
  config => {
    return config
  },
  // 對請求錯誤做些什麼
  error => {
    return Promise.reject(error)
  }
)

// 匯出帶有token的 HTTP 請求頭
export function setHttpToken(token) {
  httpRequest.defaults.headers.common.Authorization = `Bearer ${token}`
}

// 新增響應攔截器
httpRequest.interceptors.response.use(
  // 對響應資料做點什麼
  // 如果返回的狀態碼為200,說明介面請求成功,可以正常拿到資料     
  // 否則的話丟擲錯誤
  response => {
    return response
  },
  // 對響應錯誤做點什麼
  // 伺服器狀態碼不是2開頭的的情況
  // 這裡可以跟你們的後臺開發人員協商好統一的錯誤狀態碼    
  // 然後根據返回的狀態碼進行一些操作,例如登入過期提示,錯誤提示等等
  error => {
    let message = error.response.data.message ? error.response.data.message : error.response.statusText
    let dangerouslyUseHTMLString = false

    // 錯誤程式碼 422
    if (error.response.status === 422 && error.response.data.hasOwnProperty('errors')) {
      message += '<br>';
      for (let key in error.response.data.errors) {
        let items = error.response.data.errors[key]
        if (typeof items === 'string') {
          message += `${items} <br>`
        } else {
          error.response.data.errors[key].forEach( item => {
            message += `${item} <br>`
          })
        }
      }
      dangerouslyUseHTMLString = true
    }
    // 錯誤程式碼 401
    if (error.response.status === 401 && error.response.data.message === 'Unauthenticated.') {
      router.push({name: 'authLogin'})
    }

    Message({
      dangerouslyUseHTMLString,
      message: message,
      type: 'error'
    })

    return Promise.reject(error)
  }
)

export default httpRequest

新建一個登陸Api檔案src/api/login.js

import http from '../utils/http'

export const login = ({ username, password, provider }) => {
  return http.post('/oauth/token', {
    username,
    password,
    provider,
    grant_type: 'password',
    client_id: process.env.VUE_APP_AUTH_CLIENT_ID,
    client_secret: process.env.VUE_APP_AUTH_CLIENT_SECRET
  })
}

對localforage封裝一個工具src/utils/auth/index.js

import localforage from 'localforage'

const TOKEN = 'token';

// 當值被儲存後
export const setToken = (token) => {
  return localforage.setItem(TOKEN, token)
}

// 當離線倉庫中的值被載入時
export const getToken = () => {
  return localforage.getItem(TOKEN)
}

// 當值被移除後
export const removeToken = () => {
  return localforage.removeItem(TOKEN)
} 

新建src/store/modules/auth.js

import { login } from '../../api/login'
import { removeToken, setToken } from '../../utils/auth'

const state = {
  token: '',
}

const getters = {
  //token
  token: state => state.token,
  //授權token
  accessToken : state => state.token.access_token,
  //授權的型別
  provider: state => state.token.provider,
  //判斷是否登入
  isLogin: state => state.token.created_at + state.token.expires_in * 1000 > new Date().getTime(),
}

const mutations = {
  SET_TOKEN (state, {token}) {
    state.token = token
  }
}

const actions = {
  //登入
  loginHandle ({ commit }, { username, password, provider }) {
    return new Promise((resolve, reject) => {
      return login({username, password, provider})
        .then(response => {
          const token = {
            ...response.data,
            created_at: new Date().getTime(),
            provider
          }

          commit('SET_TOKEN', {token})

          resolve(setToken(token))
        })
        .catch(error => {
          reject(error)
        })
    })
  },

  logoutHandle ({ commit }) {
    return new Promise(() => {
      removeToken()
      commit("SET_TOKEN", { token: {} })
    })
  }
}

export default {
  state, // state 類似與元件中的 data 資料
  getters, // getter 存放公共函式供元件呼叫,類似於元件中的過濾函式 computed 或者 filters
  mutations, // mutations 類似於元件裡面的 methods 在 mutations 裡面可以對 state 的資料進行修改
  actions // actions 類似於mutations 大體歸類到事件,mutation像事件註冊,需要相應的條件觸發,action像是管理觸發條件。
} 

新建src/store/plugin.js

import { setHttpToken } from '../utils/http'

const subscribe = (store) => {
  store.subscribe((mutation, state) => {
    switch (mutation.type) {
      case 'SET_TOKEN':
        setHttpToken(state.auth.token.access_token)
    }
  })
}

export default (store) => {
  subscribe(store)
}; 

新建src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import auth from './modules/auth'
import plugin from './plugin'

Vue.use(Vuex)

// 建立 store 物件
const store = new Vuex.Store({
  modules: {
    auth,
  },
  plugins: [plugin]
})

// 匯出 store 物件
export default store

新建獲取學生資料的src/api/student.js

import http from '../utils/http'

// 獲取全部學生資料
export const allStudent = () => {
  return http.get('/api/student')
}

// 獲取學生資料
export const me = () => {
  return http.get('/api/student-me')
}

// 獲取學生關注老師的資料
export const teachers = () => {
  return http.get("/api/subscribe-teachers")
}

 修改前端現實的頁面src/views/home/home.vue

 <template>
  <div>
    <el-tabs type="border-card">
      <el-tab-pane label="老師列表">
        <el-table
                :data="teachers"
                style="width: 100%">
          <el-table-column
                  prop="name"
                  label="老師名稱">
          </el-table-column>
          <el-table-column
                  fixed="right"
                  label="操作"
                  width="200">
            <template slot-scope="scope">
              <el-button @click="handleClick(scope.row)" type="text" size="small">關注</el-button>
              <el-button type="text" size="small">取消關注</el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-tab-pane>
      <el-tab-pane label="學生列表">
        <el-table
                :data="students"
                style="width: 100%">
          <el-table-column
                  prop="name"
                  label="老師名稱">
          </el-table-column>
          <el-table-column
                  fixed="right"
                  label="操作"
                  width="200">
            <template slot-scope="scope">
              <el-button @click="handleClick(scope.row)" type="text" size="small">關注</el-button>
              <el-button type="text" size="small">取消關注</el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-tab-pane>
    </el-tabs>

  </div>
</template>

<script>
import { allTeacher } from "../../api/teacher"
import { allStudent } from "../../api/student"

  export default {
    name: "home",
    data() {
      return {
        students: [],
        teachers: []
      }
    },
    methods: {
      /*handleSelect(key, keyPath) {
        console.log(key, keyPath)
      }*/
    },
    created() { //資料初始化
      allTeacher().then(response => {
        this.teachers = response.data.data
      })
      allStudent().then(response => {
        this.students = response.data.data
      })
    }
  }
</script>

<style scoped>

</style>

即可獲得後臺傳進來的資料,顯示如下

Laravel+vue.js前後端分離解決跨域後獲取資料庫使用者列表(五)

本作品採用《CC 協議》,轉載必須註明作者和本文連結

一直在學習,從未停止,終身學習。

相關文章