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

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

《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
最佳答案

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

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

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

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

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

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)

4年前 评论
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 里面

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

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

4年前 评论

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