身份验证 - 调用 $http 的 .then() 成功回调而不是错误回调

Authentication - $http's .then() success callback called instead of error callback

这可能只是因为误解了如何在 MEAN 堆栈应用程序中最好地进行身份验证,或者我对 promises 和 $http 的 .then() 方法如何工作缺乏了解,但每当我尝试进行身份验证时我的后端节点服务器凭据不正确,它正在调用 $http 的 .then() 方法的成功回调而不是错误回调。这是我的设置:

我正在使用 jsonwebtokenexpress-jwt 包,AngularJS 拦截器将令牌添加到请求并检查状态 401 responseErrors,一个 TokenService sets/removes, 等等 JWTs, 和一个 UserService 来处理登录, 注销等.

根据调试,这是正在发生的事情:

  1. 登录请求已发出
  2. 服务器捕获请求,查找指定用户,但在数据库中找不到他们。 Returns 401 错误 JSON 对象包括错误消息等
  3. HttpInterceptor 使用 responseError 方法,正确地看到它是状态 401,删除任何可能的现有令牌,重定向到 /login 屏幕,然后 returns $q.reject(response)
  4. UserService.login() 正确使用错误回调并执行 return response.
  5. 问题 - 我的 login.js .login() 方法 运行 中的成功回调,而不是第二个错误回调。我觉得这与 this article about promise chaining 中讨论的内容有关,但我的专业知识在这里有其局限性,我不太明白接下来我应该做什么来告诉链中的下一个回调上一个有错误...

这是我的设置:

快递:

authRoutes.js

authRoutes.post("/login", function (req, res) {

    User.findOne({username: req.body.username}, function (err, user) {
        if (err) res.status(500).send(err);
        if (!user) {
            res.status(401).send({success: false, message: "User with the provided username was not found"})
        } else if (user) {
            bcrypt.compare(req.body.password, user.password, function (err, match) {
                if (err) throw (err);
                if (!match) res.status(401).json({success: false, message: "Incorrect password"});
                else {
                    var token = jwt.sign(user, config.secret, {expiresIn: "24h"});
                    res.json({token: token, success: true, message: "Here's your token!"})
                }
            });
        }
    });
});

从调试来看,当我使用错误的凭据登录时,它正确地命中了 res.status(401).send(...) 行,所以这部分似乎没问题。

Angular:

app.js(包括 HttpInterceptor)

var app = angular.module("TodoApp", ["ngRoute"]);

app.factory("AuthInterceptor", ["$q", "$location", "TokenService", function ($q, $location, TokenService) {
    return {
        request: function (config) {
            var token = TokenService.getToken();
            if (token) {
                config.headers = config.headers || {};
                config.headers.Authorization = "Bearer " + token
            }
            return config;
        },
        responseError: function (response) {
            if (response.status === 401) {
                TokenService.removeToken();
                $location.path("/login");
            }
            return $q.reject(response);
        }
    }
}]);

app.config(function ($routeProvider, $httpProvider) {
    $httpProvider.interceptors.push('AuthInterceptor');

    $routeProvider
        .when("/", {
            templateUrl: "landing/landing-page.html"
        });
});

userService.js

var app = angular.module("TodoApp");

app.service("UserService", ["$http", "TokenService", function ($http, TokenService) {

    this.signup = function (user) {
        return $http.post("http://localhost:8080/auth/signup", user).then(function (response) {
            return response;
        }, function (response) {
            return response;
        });
    };

    this.login = function (user) {
        return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
            if (response.data.success) TokenService.setToken(response.data.token);
            return response;
        }, function (response) {
            return response;
        })
    };

    this.isAdmin = function (user) {
        return user.admin;
    };
}]);

login.js(问题似乎已经显现)

var app = angular.module("TodoApp");

app.config(function ($routeProvider) {
    $routeProvider
        .when("/login", {
            templateUrl: "auth/login.html",
            controller: "LoginController"
        })
});

app.controller("LoginController", ["$scope", "$http", "$location", "UserService", "TokenService", function ($scope, $http, $location, UserService, TokenService) {

    $scope.login = function (user) {
        UserService.login(user).then(function (response) {
            $location.path("/todo");
        }, function (response) {
            console.log("There was a problem: " + response);
        });
    }
}]);

最后一部分,UserService.login(user).then(function (response) { $location.path("/todo"); 是 运行 试图将用户重定向到 Todo 项目列表的行,当我 want 它改为 运行 那 console.log("There was a problem: " + response); 行...

就像我上面说的,我觉得它与链接承诺以及如何在链中途处理错误而不是通过链向下冒泡有关。不确定我是否需要像上面提到的网站所说的那样添加 .catch() 块。即使那是答案,我也不确定如何写。

如果我有更好的组织方式,我也绝对乐于接受建议。我必须将此教给 class 名学生,并希望确保我教的是好的做法。

在此先感谢您的帮助!

您是否尝试过在 then() 调用的错误情况下使用 $q.reject

例如

// remember to add $q to deps

this.login = function (user) {
    return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
        if (response.data.success) TokenService.setToken(response.data.token);
        return response;
    }, function (response) {
        $q.reject(response);
    })
};

相关文档:https://docs.angularjs.org/api/ng/service/$q

仔细查看您的这部分代码:

this.login = function (user) {
    return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
        if (response.data.success) TokenService.setToken(response.data.token);
        return response;
    }, function (response) {
        return response;
    })
}

在这里,您提供了带有 return 值的错误回调,该值传递给承诺链中的下一个回调。你困惑的根源是,如果你想让 error 进一步传播,你仍然需要 return rejected promise of throw from the callback。否则,这实际上意味着您已从错误情况中恢复,流程中的下一步将会成功。这就是你现在所拥有的。

在你的情况下,你要么完全删除错误回调

return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
    if (response.data.success) TokenService.setToken(response.data.token);
    return response;
});

... 或确保您 return 承诺失败

return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
    if (response.data.success) TokenService.setToken(response.data.token);
    return response;
}, function (response) {
    return $q.reject(response);
});

... 或抛出:

return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
    if (response.data.success) TokenService.setToken(response.data.token);
    return response;
}, function (response) {
    throw new Error(response);
});