Laravel Vuejs 實戰:開發知乎 (21)前后端分離 API token 認證


解決上一節當中如果api路由改成:

  1 Route::middleware('auth:api')->post('/questions/follow', 'QuestionController@followThroughApi');

之后 axios ajax post請求報 401 unauthorized Vampire bat異常的問題。

原理Open-mouthed smile

Laravel Passport

Passport OAuth 認證

教程School

Building SPAs with Laravel 5 and Vue.js 2 - Finishing Up

API 認證解決方案:Laravel Passport

API Authentication in Laravel-Vue SPA using Jwt-auth

再增加一個:OAuth2 認證機制 Token 原理 以及 Laravel 安裝 Passport 身份驗證

修改指導:簡單來說,auth要求的token我們沒有提供Crying face

批注 2020-03-01 195846

通過api訪問走的是token認證,這里沒有提供token所以就認證失敗返回401了

這個tokendriver對應的其實就是:

批注 2020-03-01 200051

這個TokenGuard.php文件,這里面需要一個api_token。需要在request里提供api_token參數,為了區別是哪個用戶,需要在user表添加api_token字段。認證過程調用的是TokenGuard.php中的getTokenForRequest方法:

批注 2020-03-01 202202

這個bearerToken實際找header中是否存在Authorization

批注 2020-03-01 202301

我們需要來提供這個token:

原理參考:

BearerToken:

本質上給用戶表添加api_token,后台根據這個字段判斷是否是有效的用戶,無效返回401,有效返回查詢結果。
優點是容易理解,缺點太簡單,安全也不夠。
為了安全,可以實現下面的功能:

每次登錄成功后刷新api_token為新值
其實 Laravel 官方提供了一個
Laravel Passport 的包。Laravel Passport is an OAuth2 server and API authentication package 。

Laravel Vue 前后端分離 使用token認證 

搞一搞laravel里api路由的 auth:api 和 api_token

Laravel API Token 體驗

JWT(Json Web Token):

Laravel 5 中使用 JWT(Json Web Token) 實現基於API的用戶認證

【但是Thumbs down並不建議真實生產環境下用下面的方法,建議用官方的passport或者jwtPointing up

步驟:

執行命令:

  1 php artisan make:migration add_api_token_to_users_table --table=users

編輯****_add_api_token_to_users_table.php文件:

  1 <?php
  2 
  3 use Illuminate\Database\Migrations\Migration;
  4 use Illuminate\Database\Schema\Blueprint;
  5 use Illuminate\Support\Facades\Schema;
  6 
  7 class AddApiTokenToUsersTable extends Migration
  8 {
  9     /**
 10      * Run the migrations.
 11      *
 12      * @return void
 13      */
 14     public function up()
 15     {
 16         Schema::table('users', function (Blueprint $table) {
 17             //
 18             $table->string('api_token', 64)->unique()->comment("api驗證token");
 19         });
 20     }
 21 
 22     /**
 23      * Reverse the migrations.
 24      *
 25      * @return void
 26      */
 27     public function down()
 28     {
 29         Schema::table('users', function (Blueprint $table) {
 30             //
 31             $table->dropColumn('api_token');
 32         });
 33     }
 34 }
 35 
 36 
_add_api_token_to_users_table.php

執行命令:

  1 php artisan migrate

其余的參考Laravel Vue 前后端分離 使用token認證 按照此鏈接教程,完成后打開網頁刷新,再點擊關注按鈕,axios提交的時候不會報401異常了,記得修改 api.php中:

  1 Route::middleware('auth:api')->post('/questions/follow', 'QuestionController@followThroughApi');


修改一下之前的兩個錯誤:

1.因為之前采用的是

一個路由 處理刷新取關注狀態和按下按鈕關注的邏輯,這樣一刷新頁面就又相當於執行了一次關注/取關操作,必須分割開

  1 //加載頁面時取關注狀態
  2 Route::middleware('auth:api')->post('/questions/follow/stats', 'QuestionController@getFollowStats');
  3 //執行關注/取關操作
  4 Route::middleware('auth:api')->post('/questions/follow', 'QuestionController@followThroughApi');
  5 

然后,對應需要在QuestionController添加一個方法取關注狀態,

2.之前判斷用戶可否關注的邏輯錯誤

  1 public function getFollowStats(Request $request)
  2 {
  3     $user = auth()->guard('api')->user();
  4     $question = Question::find($request->get('question'));
  5 
  6     $followable = $user->can('follow', $question);
  7 
  8     return response()->json([
  9         'followable' => $followable,
 10     ]);
 11 }
 12 
 13 public function followThroughApi(Request $request)
 14 {
 15     $user = auth()->guard('api')->user();
 16     $question = Question::find($request->get('question'));
 17 
 18     //同步記錄
 19     $user->followQuestions()->toggle($question->id);
 20     $question->followers_count = $question->followUsers()->count();
 21     $question->update();
 22     //判斷用戶關注狀態
 23     $followable = $user->can('follow', $question);
 24 
 25     return response()->json([
 26         'followable' => $followable,
 27     ]);
 28 }
 29 

再然后,優化一下,用戶的數據不由props傳遞,也不由axios提交,因為有bearer token了。


接着QuestionController中followThroughApi和getFollowStats方法內,

$user獲取: 直接通過 auth()->user()auth()->guard('api')->user(); 獲取即可。


QuestionController.php

  1 <?php
  2 
  3 namespace App\Http\Controllers;
  4 
  5 use App\Http\Requests\QuestionStoreRequest;
  6 use App\Models\Question;
  7 use App\Repositories\QuestionRepository;
  8 use App\User;
  9 use Illuminate\Http\Request;
 10 
 11 class QuestionController extends Controller
 12 {
 13 
 14     /**
 15      * @var QuestionRepository
 16      */
 17     private $questionRepository;
 18 
 19     public function __construct(QuestionRepository $questionRepository)
 20     {
 21         $this->middleware(
 22             'auth',
 23             [
 24                 'except' =>
 25                     [
 26                         'index',
 27                         'show',
 28                         'followThroughApi'
 29                     ]//非注冊用戶只能查看不能編輯添加更改刪除
 30             ]
 31         );
 32 
 33         $this->questionRepository = $questionRepository;
 34     }
 35 
 36 
 37     /** Display a listing of the resource.
 38      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 39      */
 40     public function index()
 41     {
 42         //
 43         $questions = $this->questionRepository->getQuestionPublished();
 44         return view('questions.index', compact('questions'));
 45     }
 46 
 47 
 48     /**
 49      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 50      */
 51     public function create()
 52     {
 53         //
 54         return view('questions.create');
 55     }
 56 
 57 
 58     /**
 59      * @param QuestionStoreRequest $request
 60      * @return \Illuminate\Http\RedirectResponse
 61      */
 62     public function store(QuestionStoreRequest $request)//依賴注入QuestionStoreRequest實例
 63     {
 64         //
 65 //        $data = $request->validate([
 66 //            'title' => 'required|min:8',
 67 //            'content' => 'required|min:28',
 68 //        ]);
 69         //存儲topics
 70         $topics = $this->questionRepository->normalizeTopics($request->get('topics'));
 71         //初始化question要用到的數據
 72         $data = $request->all();
 73         $data['user_id'] = auth()->user()->id;
 74 
 75 //        $question=Question::create($data); 被下方代碼取代
 76         $question = $this->questionRepository->create($data);
 77 
 78         //使用我們再question model里面添加的topics方法獲得 topics關聯,再使用attach方法
 79         $question->topics()->attach($topics);
 80 
 81         return redirect()->route('questions.show', $question);
 82     }
 83 
 84 
 85     /**
 86      * @param Question $question
 87      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 88      */
 89     public function show(Question $question)
 90     {
 91         //使用關系關聯加載,with方法會將分類之下的主題一起查詢出來,而且不會出現N+1影響性能的問題
 92         $question->with('topics')->get();
 93         //使用關系關聯加載,with方法會將分類之下的回答一起查詢出來,而且不會出現N+1影響性能的問題
 94         $question->with('answers')->get();
 95 
 96         return view('questions.show', compact('question'));
 97     }
 98 
 99 
100     /**判斷權限 返回視圖
101      * @param Question $question
102      * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
103      */
104     public function edit(Question $question)
105     {
106         if (auth()->user()->can('update', $question)) //判斷當前用戶是否有權編輯更新該question實例
107         {
108             //返回編輯視圖
109             return view('questions.edit', compact('question'));
110         } else {
111             //返回警告 沒有權限
112             return redirect()->back()->with('warning', '你不能編輯不屬於你的問題!');
113         }
114     }
115 
116 
117     /** Update the specified resource in storage.
118      * @param QuestionStoreRequest $questionStoreRequest
119      * @param Question $question
120      * @return \Illuminate\Http\RedirectResponse
121      */
122     public function update(QuestionStoreRequest $questionStoreRequest, Question $question)
123     {
124         //更新前 判斷下權限
125         if (!(auth()->user()->can('update', $question))) {
126             //返回警告 沒有權限
127             return redirect()->back()->with('warning', '你不能編輯不屬於你的問題!');
128         }
129         //取得更新的字段 使用Eloquent提供的update方法執行問題更新
130         $question->update([
131             'title' => $questionStoreRequest->get('title'),
132             'content' => $questionStoreRequest->get('content'),
133         ]);
134 
135 
136         //topics的操作這時候看起來有點臃腫 可以使用TopicController來管理,暫時省略
137         //存儲topics
138         $topics = $this->questionRepository->normalizeTopics($questionStoreRequest->get('topics'));
139         //使用我們再question model里面添加的topics方法獲得 topics關聯,
140         //再使用sync方法同步tag 【刪除的會被刪除掉,沒刪除的就保留,新的就增加】
141         $question->topics()->sync($topics);
142 
143         //更新完成,跳轉回去
144         return redirect()->back();
145     }
146 
147 
148     /**Remove the specified resource from storage.
149      * @param Question $question
150      * @return \Illuminate\Http\RedirectResponse
151      * @throws \Exception
152      */
153     public function destroy(Question $question)
154     {
155         //
156         if (auth()->user()->can('destroy', $question)) {
157             $question->delete();
158             return redirect()->route('questions.index')->with('success', "刪除成功!");
159         }
160         return redirect()->back()->with('danger', "你不能刪除不屬於你的問題!");
161     }
162 
163 
164     public function follow(Question $question)
165     {
166         if (auth()->user()->can('follow', $question)) //通過QuestionPolicy的follow方法判斷用戶是否可以關注問題
167         {
168             $message = "關注";
169         } else {
170             $message = "取關";
171         }
172         //同步記錄
173         auth()->user()->followQuestions()->toggle($question);
174         $question->followers_count = $question->followUsers()->count();
175         $question->save();
176         return redirect()->back()->with('success', $message . '成功!');
177     }
178 
179     public function getFollowStats(Request $request)
180     {
181         $user = auth()->guard('api')->user();
182         $question = Question::find($request->get('question'));
183 
184         $followable = $user->can('follow', $question);
185 
186         return response()->json([
187             'followable' => $followable,
188         ]);
189     }
190 
191     public function followThroughApi(Request $request)
192     {
193         $user = auth()->guard('api')->user();
194         $question = Question::find($request->get('question'));
195 
196         //同步記錄
197         $user->followQuestions()->toggle($question->id);
198         $question->followers_count = $question->followUsers()->count();
199         $question->update();
200         //判斷用戶關注狀態
201         $followable = $user->can('follow', $question);
202 
203         return response()->json([
204             'followable' => $followable,
205         ]);
206     }
207 }
208 
209 
QuestionController.php

QuestionPolicy.php

  1 <?php
  2 
  3 namespace App\Policies;
  4 
  5 use App\Models\Question;
  6 use App\User;
  7 use Illuminate\Auth\Access\HandlesAuthorization;
  8 
  9 class QuestionPolicy
 10 {
 11     use HandlesAuthorization;
 12 
 13     /**
 14      * Create a new policy instance.
 15      *
 16      * @return void
 17      */
 18     public function __construct()
 19     {
 20         //
 21 
 22     }
 23 
 24 
 25     /**
 26      * 判斷用戶是否有權編輯更新問題
 27      * @param User $user
 28      * @param Question $question
 29      * @return bool
 30      */
 31     public function update(User $user, Question $question)
 32     {
 33         return $user->id === $question->user_id;
 34     }
 35 
 36 
 37     /**
 38      * 判斷用戶是否有權刪除問題
 39      * @param User $user
 40      * @param Question $question
 41      * @return bool
 42      */
 43     public function destroy(User $user, Question $question)
 44     {
 45         return $user->id === $question->user_id;
 46     }
 47 
 48 
 49     /** 用戶是否可以關注問題,未登錄不行,關注了不行
 50      * @param User $user
 51      * @param Question $question
 52      * @return bool
 53      */
 54     public function follow(User $user, Question $question)
 55     {
 56         //axiox api 需要auth:api 先不實現,注釋掉
 57         if (auth()->check()) {
 58             return !($user->followQuestions->contains('id', $question->id));
 59         } else {
 60 
 61         }
 62     }
 63 }
 64 
 65 
QuestionPolicy.php

app.blade.php

  1 <!doctype html>
  2 <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
  3 <head>
  4     <meta charset="utf-8">
  5     <meta name="viewport" content="width=device-width, initial-scale=1">
  6 
  7     {{--    CSRF Token--}}
  8     <meta name="csrf-token" content="{{ csrf_token() }}">
  9     {{--    api bearer Token--}}
 10     <meta name="api-token" content="{{ Auth::check() ? 'Bearer '.auth()->user()->api_token : 'Bearer ' }}">
 11 
 12     <title>{{ config('app.name', 'Laravel') }}</title>
 13 
 14 
 15     {{--    Fonts--}}
 16     <link rel="dns-prefetch" href="//fonts.gstatic.com">
 17     <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
 18 
 19     {{--    Styles--}}
 20     <link href="{{ mix('css/app.css') }}" rel="stylesheet">
 21 
 22 </head>
 23 <body>
 24 <div id="app">
 25     <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
 26         <div class="container">
 27             <a class="navbar-brand" href="{{ url('/') }}">
 28                 {{ config('app.name', 'Laravel') }}
 29             </a>
 30             <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
 31                     aria-controls="navbarSupportedContent" aria-expanded="false"
 32                     aria-label="{{ __('Toggle navigation') }}">
 33                 <span class="navbar-toggler-icon"></span>
 34             </button>
 35 
 36             <div class="collapse navbar-collapse" id="navbarSupportedContent">
 37                 {{--                Left Side Of Navbar--}}
 38                 <ul class="navbar-nav mr-auto">
 39 
 40                 </ul>
 41 
 42                 {{--                Right Side Of Navbar--}}
 43                 <ul class="navbar-nav ml-auto">
 44                     {{--                    Authentication Links--}}
 45                     @guest
 46                         <li class="nav-item">
 47                             <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
 48                         </li>
 49                         @if (Route::has('register'))
 50                             <li class="nav-item">
 51                                 <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
 52                             </li>
 53                         @endif
 54                     @else
 55                         <li class="nav-item dropdown">
 56                             <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button"
 57                                data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
 58                                 {{ Auth::user()->name }} <span class="caret"></span>
 59                             </a>
 60 
 61                             <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
 62                                 <a class="dropdown-item" href="{{ route('logout') }}"
 63                                    onclick="event.preventDefault();
 64                                                      document.getElementById('logout-form').submit();">
 65                                     {{ __('Logout') }}
 66                                 </a>
 67 
 68                                 <form id="logout-form" action="{{ route('logout') }}" method="POST"
 69                                       style="display: none;">
 70                                     @csrf
 71                                 </form>
 72                             </div>
 73                         </li>
 74                     @endguest
 75                 </ul>
 76             </div>
 77         </div>
 78     </nav>
 79 
 80     <main class="py-4">
 81         @include('flash::message')
 82         @yield('content')
 83     </main>
 84 </div>
 85 {{--Scripts--}}
 86 <script src="{{ mix('js/app.js') }}"></script>
 87 
 88 <script>
 89     $('#flash-overlay-modal').modal();
 90     window.UEDITOR_CONFIG.serverUrl = "{{ config('ueditor.route.name') }}";
 91 </script>
 92 @yield('footer-js')
 93 </body>
 94 </html>
 95 
 96 
app.blade.php

api.php

  1 <?php
  2 
  3 use Illuminate\Http\Request;
  4 
  5 /*
 6 |--------------------------------------------------------------------------
 7 | API Routes
 8 |--------------------------------------------------------------------------
 9 |
 10 | Here is where you can register API routes for your application. These
 11 | routes are loaded by the RouteServiceProvider within a group which
 12 | is assigned the "api" middleware group. Enjoy building your API!
 13 |
 14 */
 15 
 16 Route::middleware('auth:api')->get('/user', function (Request $request) {
 17     return $request->user();
 18 });
 19 
 20 Route::middleware('api')->get('/topics', function (Request $request) {
 21     $query = $request->query('q');
 22     return \App\Topic::query()->where('name', 'like', '%' . $query . '%')->get();
 23 });
 24 //加載頁面時取關注狀態
 25 Route::middleware('auth:api')->post('/questions/follow/stats', 'QuestionController@getFollowStats');
 26 //執行關注/取關操作
 27 Route::middleware('auth:api')->post('/questions/follow', 'QuestionController@followThroughApi');
 28 
 29 
 30 
 31 
api.php

bootstrap.js

  1 window._ = require('lodash');
  2 
  3 /**
 4  * We'll load jQuery and the Bootstrap jQuery plugin which provides support
 5  * for JavaScript based Bootstrap features such as modals and tabs. This
 6  * code may be modified to fit the specific needs of your application.
 7  */
  8 
  9 try {
 10     window.Popper = require('popper.js').default;
 11     window.$ = window.jQuery = require('jquery');
 12 
 13     require('bootstrap');
 14 } catch (e) {
 15 }
 16 
 17 /**
 18  * We'll load the axios HTTP library which allows us to easily issue requests
 19  * to our Laravel back-end. This library automatically handles sending the
 20  * CSRF token as a header based on the value of the "XSRF" token cookie.
 21  */
 22 
 23 window.axios = require('axios');
 24 
 25 window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
 26 
 27 let api_token = document.head.querySelector('meta[name="api-token"]');
 28 
 29 if (api_token) {
 30     window.axios.defaults.headers.common['Authorization'] = api_token.content;
 31 } else {
 32     console.error('Authorization token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
 33 }
 34 
 35 /**
 36  * Echo exposes an expressive API for subscribing to channels and listening
 37  * for events that are broadcast by Laravel. Echo and event broadcasting
 38  * allows your team to easily build robust real-time web applications.
 39  */
 40 
 41 // import Echo from 'laravel-echo';
 42 
 43 // window.Pusher = require('pusher-js');
 44 
 45 // window.Echo = new Echo({
 46 //     broadcaster: 'pusher',
 47 //     key: process.env.MIX_PUSHER_APP_KEY,
 48 //     cluster: process.env.MIX_PUSHER_APP_CLUSTER,
 49 //     encrypted: true
 50 // });
 51 
 52 
bootstrap.js

QuestionFollowButton.vue

  1 <template>
  2     <button :class="classObject"
  3             @click="follow"
  4             v-text="text">
  5     </button>
  6 </template>
  7 
  8 <script>
  9     export default {
 10         props: ['question'],
 11         name: "QuestionFollowButton",
 12         data() {
 13             return {
 14                 followable: true,
 15             }
 16         },
 17         computed: {
 18             text() {
 19                 return this.followable ? "關注用戶" : "取消關注";
 20             },
 21             classObject() {
 22                 return this.followable ? "btn btn-block btn-primary" : "btn btn-block btn-danger";
 23             },
 24         },
 25         mounted: function () {
 26             let currentObj = this;
 27             axios.post('/api/questions/follow/stats', {'question': this.question})
 28                 .then(function (response) {
 29                     currentObj.followable = response.data.followable;
 30                 })
 31                 .catch(function (e) {
 32                     console.log(e);
 33                 });
 34         },
 35         methods: {
 36             follow() {
 37                 let currentObj = this;
 38                 axios.post('/api/questions/follow', {'question': this.question})
 39                     .then(function (response) {
 40                             currentObj.followable = response.data.followable;
 41                         }
 42                     )
 43                     .catch(function (e) {
 44                         console.log(e);
 45                     });
 46             },
 47         }
 48     }
 49 </script>
 50 
 51 <style scoped>
 52 
 53 </style>
 54 
 55 
QuestionFollowButton.vue

show.blade.php

  1 @extends('layouts.app')
  2 @section('content')
  3     <div class="container">
  4         <div class="row">
  5             <div class="col-md-8 col-md offset-1">
  6                 {{--問題--}}
  7                 <div class="card">
  8                     <div class="card-header">
  9                         {{ $question->title }}
 10 
 11                         @foreach(['success','warning','danger'] as $info)
 12                             @if(session()->has($info))
 13                                 <div class="alert alert-{{$info}}">{{ session()->get($info) }}</div>
 14                             @endif
 15                         @endforeach
 16 
 17                         @can('update',$question)
 18                             <a href="{{ route('questions.edit',$question) }}" class="btn btn-warning">編輯</a>
 19                         @endcan
 20 
 21                         @can('destroy',$question)
 22                             <form action="{{ route('questions.destroy',$question) }}" method="post">
 23                                 @csrf
 24                                 @method('DELETE')
 25                                 <button type="submit" class="btn btn-danger">刪除</button>
 26                             </form>
 27                         @endcan
 28 
 29                         @forelse($question->topics as $topic)
 30                             <button class="btn btn-secondary float-md-right m-1">{{ $topic->name }}</button>
 31                         @empty
 32                             <p class="text text-warning float-md-right"> "No Topics"</p>
 33                         @endforelse
 34 
 35                         <p class="text text-info float-md-right"> 已有{{ count($question->answers) }}個回答</p>
 36 
 37                     </div>
 38                     <div class="card-body">
 39                         {!! $question->content !!}
 40                     </div>
 41                 </div>
 42 
 43 
 44                 {{--回答提交form--}}
 45                 {{--只有登錄用戶可以提交回答--}}
 46                 @if(auth()->check())
 47                     <div class="card mt-2">
 48                         <div class="card-header">
 49                             提交回答
 50                         </div>
 51                         <div class="card-body">
 52                             <form action="{{ route('answers.store',$question) }}" method="post">
 53                             @csrf
 54                             <!-- 回答編輯器容器 -->
 55                                 <script id="container" name="content" type="text/plain"
 56                                         style="width: 100%;height: 200px">{!! old('content') !!}</script>
 57                                 <p class="text text-danger"> @error('content') {{ $message }} @enderror </p>
 58                                 <!--提交按鈕-->
 59                                 <button type="submit" class="btn btn-primary float-md-right mt-2">提交回答</button>
 60                             </form>
 61                         </div>
 62                     </div>
 63                 @else
 64                     {{--顯示請登錄--}}
 65                     <a href="{{ route('login') }}" class="btn btn-success btn-block mt-4">登錄提交答案</a>
 66                 @endif
 67                 {{--展示答案--}}
 68                 @forelse($question->answers as $answer)
 69                     <div class="card mt-4">
 70                         <div class="card-header">
 71                             <div class="float-left">
 72                                 <img src="{{ $answer->user->avatar }}" class="img-thumbnail imgWrap"
 73                                      style="height: 50px" alt="{{ $answer->user->name }}">
 74                                 <span class="text text-info">{{ $answer->user->name }}</span>
 75                             </div>
 76                             <span class="float-right text text-info m-auto">{{ $answer->updated_at }}</span>
 77                         </div>
 78 
 79                         <div class="card-body">
 80                             {!!  $answer->content  !!}
 81                         </div>
 82                     </div>
 83 
 84                 @empty
 85 
 86                 @endforelse
 87             </div>
 88 
 89             <div class="col-md-3">
 90                 <div class="card">
 91                     <div class="card-header">
 92                         <h2> {{ $question->followers_count }}</h2>
 93                         <span>關注者</span>
 94                     </div>
 95 
 96                     <div class="card-body">
 97                         <div id="app">
 98                             <question-follow-button question="{{$question->id}}">
 99                             </question-follow-button>
100                         </div>
101                     </div>
102                 </div>
103             </div>
104         </div>
105     </div>
106 @endsection
107 @section('footer-js')
108     @include('questions._footer_js')
109 @endsection
110 
111 
show.blade.php

補充:

截取

API requests with axios always unauthorized with Laravel API 

I'm not using Passort or any library like that since it's an internal API serving only VueJS to obtain stuff from the database.

If the API is not stateless, meaning that the user is known to be logged in with a standard session cookie, then you can just use the default 'web' middleware for the API routes.

In the default RouteServiceProvider, change the mapApiRoutes function to use the web middleware instead:

protected function mapApiRoutes()
{
    Route::prefix('api')
        // ->middleware('api')
        ->middleware('web')
        ->namespace($this->namespace)
        ->group(base_path('routes/api.php'));
}

That being said, you should really put the API routes behind the default 'auth' middleware since they're not throttled by default.

In the routes/api.php file:

Route::group(['middleware' => 'auth'], function() {
    Route::get('/latest', 'InternalApiController@latest');
});

And if you want to ensure it's an AJAX request, you can create a simple middleware that checks that the request has the X-Requested-With header set to XMLHttpRequest.

class RequestIsAjax
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (!$request->ajax()) {
            return redirect()->route('login.index');
        }

        return $next($request);
    }
}

And register it within the $routeMiddleware array inside the \App\Http\Kernel class.

protected $routeMiddleware = [
    'ajax' => \App\Http\Middleware\RequestIsAjax::class,


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM