Laravel+vue.js 前后端分离解决跨域后获取数据库用户列表(五)

接续于 = >《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 协议》,转载必须注明作者和本文链接
一直在学习,从未停止,终身学习。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 1

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!