大家好(特别是Aurelia核心团队在这里闲逛)
我有一个使用“aurelia-http-client”的aurelia应用程序向我的后端API发出请求。
我的后端API是在Nancy上运行的基于C#的服务。
在我的前端,我将http客户端抽象到我自己的网络库中,如下所示:
import { inject } from 'aurelia-framework';
import { Router } from 'aurelia-router';
import { HttpClient } from 'aurelia-http-client';
import environment from './environment';
@inject(HttpClient, Router)
export default class httpservice {
private http: HttpClient = null;
private router: Router = null;
private authService: any = null;
private authToken: string = "";
constructor(HttpClient, Router) {
this.http = HttpClient;
this.router = Router;
HttpClient.configure(http => {
http.withBaseUrl(environment.servicebase);
});
}
public setAuthService(authService: any) {
this.authService = authService;
}
public get(url: string, authObject?: any): any {
let myAuth = this.authService ? this.authService : authObject;
let myToken = "";
if (myAuth) {
myToken = myAuth.getAuthToken();
}
let self = this;
let client = this.http
.createRequest(url)
.asGet()
.withHeader("AuthenticationToken", myToken)
.withInterceptor({
responseError(responseError) {
console.log(responseError);
if (responseError.statusCode === 401) {
if (myAuth) {
myAuth.destroySession();
}
}
if (responseError.statusCode === 404) {
self.router.navigateToRoute("missing");
}
return responseError;
}
});
return client;
}
public post(url: string, postData: any, authObject?: any): any {
let myAuth = this.authService ? this.authService : authObject;
let myToken = "";
if (myAuth) {
myToken = myAuth.getAuthToken();
}
let self = this;
let client = this.http
.createRequest(url)
.asPost().withContent(postData)
.withHeader("AuthenticationToken", myToken)
.withInterceptor({
responseError(responseError) {
console.log(responseError);
if (responseError.statusCode === 401) {
if (myAuth) {
myAuth.destroySession();
}
}
if (responseError.statusCode === 404) {
self.router.navigateToRoute("missing");
}
return responseError;
}
});
return client;
}
}
然后我在我的其他模块/类中使用它,如下所示:
import { Aurelia, inject } from 'aurelia-framework';
import HttpService from './httpservice';
import environment from './environment';
import { EventAggregator } from 'aurelia-event-aggregator';
@inject(EventAggregator, Aurelia, HttpService)
export default class Authservice {
public http: HttpService = null;
public app: Aurelia = null;
public ea: EventAggregator = null;
public authToken: any = null;
private loginUrl: string = "";
private logoutUrl: string = "";
private checkUrl: string = "";
constructor(eventAggregator, aurelia, httpService) {
this.http = httpService;
this.app = aurelia;
this.ea = eventAggregator;
this.loginUrl = "/login";
}
public getAuthToken() {
if (!sessionStorage[environment.tokenname] ||
(sessionStorage[environment.tokenname] == null)) {
return null;
}
return sessionStorage[environment.tokenname];
}
public login(loginName, password) {
let postData = {
loginName: loginName,
password: password
};
let client = this.http.post(this.loginUrl, postData);
client.send()
.then((response) => response.content)
.then((data) => {
if (data.error) {
this.ea.publish("loginMessage", { message: data.errorMessage });
return;
}
if (data.authenticationFailed) {
this.ea.publish("loginMessage", { message: "Invalid user name and/or password supplied." });
return;
}
if (data.accountSuspended) {
this.ea.publish("loginMessage", { message: "Your account has been suspended, please contact support." });
return;
}
sessionStorage[environment.tokenname] = data.token;
sessionStorage["displayedLoginName"] = data.displayName;
location.assign('#/');
this.app.setRoot('app');
})
.catch(() =>
{
debugger;
alert("Something bad happened trying to connect to server.");
});
}
public isAuthenticated() {
// TODO: hook this up to check auth token validity via rest call???
let token = this.getAuthToken();
return token !== null;
}
}
enum LoginStates {
LoginValid = 0,
BadUserNameOrPassword,
AccountSuspended
}
请注意我已从auth库中删除了一些代码以减少混淆
一般来说,所有这些都很有效。当401s和404s发生时,拦截器会被触发,如果我添加一个500,那么得到了处理,那么那里的所有东西都很好。
我遇到的问题是处理通信故障。
正如您在登录例程中所看到的那样,我有一个跟随当时的问题。
我预计如果无法访问服务器或发生其他一些基本通信故障,那么这个catch会触发而不是“then”,从而允许我处理错误,但事实并非如此。
我得到的是控制台中的内容:
更糟糕的是,我的登录例程没有中止,它实际上成功并允许显示登录页面。
似乎当库正在进行OPTIONS调用时(发生此错误时),我的用户代码都没有被考虑在内。
OPTIONS调用是成功的飞行前/ ajax请求所必需的,因此停止发生这种情况不是一种选择,我觉得如果OPTIONS调用没有中止,但是它进入了POST调用,那么我的错误然后将考虑处理。
无法捕捉到这样的错误似乎很愚蠢,尤其是在今天的设备可能超出覆盖范围或暂时脱机的移动世界中。
如果有人对如何解决这个问题有任何想法,我很乐意听到。
我的问题似乎与此有关: aurelia-fetch-client - a promise was rejected with a non-error: [object Response]
但是,我没有使用“useStandardConfiguration()”,这显然是导致这种情况的原因。我也没有使用fetch客户端,但我注意到两个客户端的API实际上是相同的,所以我想知道底层代码是否也是相似的。
答案 0 :(得分:2)
好的......所以,经过长时间艰难的头部刮擦和头发拉动之后,事实证明,整个事情实际上与报道的问题有关,并且" BlueBird承诺图书馆"这是aurelia用来管理它的承诺。
可以在此处找到BlueBird问题的链接: https://github.com/petkaantonov/bluebird/issues/990
根据BB开发人员的说法,这不是一个特别的问题,但对于遇到它的很多人来说,它确实看起来像是一个。
底线是该库不是为了抛出它直接生成的错误而设计的(如问题页面上的示例所示)
根据BB团队的正确方法是要么完全抛出一个新错误,要么从传递给promise的那个实例中派生一个新实例,然后在重新抛出之前将参数改为它。
当然,由于Aurelia的抽象,对于我们大多数人来说这不是一个选项,除非我们想要更改http客户端库代码。
这方面的一些标志需要转到" TheBlueFox"对于他/她上面的评论。
最终解决方案如下:
import { inject } from 'aurelia-framework';
import { Router } from 'aurelia-router';
import { HttpClient, Interceptor } from 'aurelia-http-client';
import environment from './environment';
import Debugger = require("_debugger");
@inject(HttpClient, Router)
export default class httpservice {
private http: HttpClient = null;
private router: Router = null;
private authService: any = null;
private authToken: string = "";
private myInterceptors: Interceptor;
constructor(HttpClient, Router) {
this.http = HttpClient;
this.router = Router;
HttpClient.configure(http => {
http.withBaseUrl(environment.servicebase);
http.withInterceptor(new HttpInterceptors());
});
}
public setAuthService(authService: any) {
this.authService = authService;
}
public get(url: string, authObject?: any): any {
let myAuth = this.authService ? this.authService : authObject;
let myToken = "";
if (myAuth) {
myToken = myAuth.getAuthToken();
}
let client = this.http
.createRequest(url)
.asGet()
.withHeader("AuthenticationToken", myToken);
return client;
}
public post(url: string, postData: any, authObject?: any): any {
let myAuth = this.authService ? this.authService : authObject;
let myToken = "";
if (myAuth) {
myToken = myAuth.getAuthToken();
}
let self = this;
let client = this.http
.createRequest(url)
.asPost().withContent(postData)
.withHeader("AuthenticationToken", myToken);
return client;
}
}
class HttpInterceptors implements Interceptor {
responceError(error)
{
if (error.statusCode === 0) {
throw new Error("Could not contact server");
}
if (error.statusCode === 401) {
// do auth handling here
}
if (error.statusCode === 404) {
// do 404 handling here
}
return error;
}
}
魔法在HttpInterceptors类中附加到我的HttpService的底部。您应该能够看到状态代码为0的检查,并且此处执行的实际操作是抛出新错误。
这个新错误被抛出的动作然后导致" catch"在实际调用http客户端被捕获。
如果你不在那一点上投掷,那么一切都会崩溃,你可以在原来的问题帖子中看到这个场景,扔掉你就可以抓住它并用用户代码处理它。
这种做事方式在aurelia-fetch-client中也很明显,因为它使用BlueBird承诺库以类似的方式工作。