addCookieToResponse () 这个函数接收了一个 null

我有一个中间件,用于验证用户是否登录的,用户每次访问接口,都会经过这个中间件验证,这时候小部分接口就会报如下错误:

addCookieToResponse () 这个函数接收了一个 null

postman 测试接口直接报 500,打断点时,根本没有经过我的接口和中间件,所以有可能是我的配置错误吗?最神奇的是,安卓那边请求接口时,有时会成功,但有时无反应,不涉及头部信息的问题。

环境:本地:homestead,线上:Ubuntu 18.04+apache2; 都是php7.2的,使用的是 php7.2-fpm 进行管理的;

我只写了一个中间件,代码如下:

<?php

namespace App\Http\Middleware;

use Closure;
use App\Models\Users\User;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Log;

class CheckToken
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // 必传参数,u_id 是一个加密的字符串
        if(empty($request->token)) {
            return JsonResponseArr('缺少必要参数 token');
        }

        // code = -2 时,说明用户不存在或者用户登陆失效,跳回登陆页面
        $token = User::where('token', $request->token)->first();

        if (!$token) {
            return JsonResponseArr('用户登陆保持已失效,请重新登陆', -2);
        }

        $uid = Crypt::decryptString($request->token);

        session()->put('uid', $uid);
        session()->save();

        return $next($request);
    }
}

上面的函数:JsonResponseArr 是我自定义函数,放在帮助类里了,如下:

function JsonResponseArr($message, $code = 0, $data = null)
{
    return response()->json([

        'code'    => $code,
        'message' => $message,
        'data'    => $data,

    ])->setEncodingOptions(JSON_UNESCAPED_UNICODE);
}

Kernel 文件如下:

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\EncryptCookies::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\EnableCrossRequestMiddleware::class,
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        // 下面是自定义 token 验证
        'token' => \App\Http\Middleware\CheckToken::class,
        // 这里无所谓,我报错的接口不涉及下面这些中间件
        'login' => \App\Http\Middleware\CheckLogin::class,
        'phone' => \App\Http\Middleware\CheckPhone::class,
        'code' => \App\Http\Middleware\CheckCode::class,
        'real-name_auth' => \App\Http\Middleware\CheckRealNameAuth::class,
    ];
}

本地访问都可以,但线上就是报错,扩展检查了,也没问题。我在社区中也看了一些,说是 addCookieToResponse 这个函数的第一个参数接收的是一个 response 对象,我想请问各位我的程序是哪里出错了呢?


2020-01-11 更新问题

今天在项目中又遇到了这种问题,就查看了一下源码:

public function handle($request, Closure $next)
{
    $this->sessionHandled = true;

    // If a session driver has been configured, we will need to start the session here
    // so that the data is ready for an application. Note that the Laravel sessions
    // do not make use of PHP "native" sessions in any way since they are crappy.
    if ($this->sessionConfigured()) {
        $request->setLaravelSession(
            $session = $this->startSession($request)
        );

        $this->collectGarbage($session);
    }

    $response = $next($request);
    var_dump($response);  // 这里

    // Again, if the session has been configured we will need to close out the session
    // so that the attributes may be persisted to some storage medium. We will also
    // add the session identifier cookie to the application response headers now.
    if ($this->sessionConfigured()) {
        $this->storeCurrentUrl($request, $session);
        echo gettype($response); // 这里
        $this->addCookieToResponse($response, $session);
    }

    return $response;
}

两个结果都是 null
后来我检查了我的接口,发现我请求的接口其实是不存在的,也就是说,本来应该报404的错误,但报了这个错误,所以应该是执行顺序的问题(具体的顺序没细看,但这个应该是先执行的),错误的接口没有任何响应,导致 addCookieToResponse 此方法第一个参数接收的参数为 null

其实还是自己程序的问题。

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
最佳答案

突然一不小心解决了问题,我配置和程序都没有问题,是因为我服务器上使用了 https ,然后做了一个重定向,当 http 访问时重定向到 https,使用 http 访问时,有时会被拦截,使用 https 则一切正常。感谢@nfangxu
的解答

5年前 评论
讨论数量: 4
nfangxu

中间件里不应该直接响应, 你可以抛出一个异常, 然后根据异常去返回

5年前 评论
lzxprogrammer (楼主) 5年前

上面那张图看不清楚,把错误信息粘贴此处:

local.ERROR: Type error: Argument 1 passed to Illuminate\Session\Middleware\StartSession::addCookieToResponse() must be an instance of Symfony\Component\HttpFoundation\Response, null given, called in /var/www/html/campus_social_api/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php on line 71 {"exception":"[object] (Symfony\Component\Debug\Exception\FatalThrowableError(code: 0): Type error: Argument 1 passed to Illuminate\Session\Middleware\StartSession::addCookieToResponse() must be an instance of Symfony\Component\HttpFoundation\Response, null given, called in /var/www/html/campus_social_api/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php on line 71 at /var/www/html/campus_social_api/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php:169)

5年前 评论
nfangxu

Illuminate\Session\Middleware\StartSession 源码

public function handle($request, Closure $next)
    {
        $this->sessionHandled = true;

        // If a session driver has been configured, we will need to start the session here
        // so that the data is ready for an application. Note that the Laravel sessions
        // do not make use of PHP "native" sessions in any way since they are crappy.
        if ($this->sessionConfigured()) {
            $request->setLaravelSession(
                $session = $this->startSession($request)
            );

            $this->collectGarbage($session);
        }

        $response = $next($request);

        // Again, if the session has been configured we will need to close out the session
        // so that the attributes may be persisted to some storage medium. We will also
        // add the session identifier cookie to the application response headers now.
        if ($this->sessionConfigured()) {
            $this->storeCurrentUrl($request, $session);

            $this->addCookieToResponse($response, $session); // 报错的是这行
        }

        return $response;
    }

你的中间件这样写

'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            'token', // 加了这一行
        ],

你定义的中间件应该在 \Illuminate\Session\Middleware\StartSession::class 后面

你这个直接返回 Response 的中间件, 可能没走到 \Illuminate\Session\Middleware\StartSession::class 里面

5年前 评论
lzxprogrammer (楼主) 5年前
lzxprogrammer (楼主) 5年前

突然一不小心解决了问题,我配置和程序都没有问题,是因为我服务器上使用了 https ,然后做了一个重定向,当 http 访问时重定向到 https,使用 http 访问时,有时会被拦截,使用 https 则一切正常。感谢@nfangxu
的解答

5年前 评论

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