• 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 IlluminateDatabaseMigrationsMigration;
      4 use IlluminateDatabaseSchemaBlueprint;
      5 use IlluminateSupportFacadesSchema;
      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 AppHttpControllers;
      4 
      5 use AppHttpRequestsQuestionStoreRequest;
      6 use AppModelsQuestion;
      7 use AppRepositoriesQuestionRepository;
      8 use AppUser;
      9 use IlluminateHttpRequest;
     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 IlluminateContractsViewFactory|IlluminateViewView
     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 IlluminateContractsViewFactory|IlluminateViewView
     50      */
     51     public function create()
     52     {
     53         //
     54         return view('questions.create');
     55     }
     56 
     57 
     58     /**
     59      * @param QuestionStoreRequest $request
     60      * @return IlluminateHttpRedirectResponse
     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 IlluminateContractsViewFactory|IlluminateViewView
     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 IlluminateContractsViewFactory|IlluminateHttpRedirectResponse|IlluminateViewView
    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 IlluminateHttpRedirectResponse
    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 IlluminateHttpRedirectResponse
    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 AppPolicies;
      4 
      5 use AppModelsQuestion;
      6 use AppUser;
      7 use IlluminateAuthAccessHandlesAuthorization;
      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 IlluminateHttpRequest;
      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 AppTopic::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=" 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  IlluminateHttpRequest $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 AppHttpKernel class.

    protected $routeMiddleware = [
        'ajax' => AppHttpMiddlewareRequestIsAjax::class,
  • 相关阅读:
    Linux文件目录结构详解
    Linux常用命令学习
    51Nod 1094 和为k的连续区间 | 水
    (转) jsp学习笔记
    Hadoop到底能做什么?怎么用hadoop?
    51Nod 1092 回文字符串 | 最长公共子序列变形
    51Nod 1067 Bash游戏 V2 | 博弈论 Bash
    51Nod 1062 序列中最大的数 | 简单DP
    51Nod 1050 循环数组最大子段和 | DP
    HDU 5643 King's Game | 约瑟夫环变形
  • 原文地址:https://www.cnblogs.com/dzkjz/p/12391574.html
Copyright © 2020-2023  润新知