fakebackend 拦截器错误消息:"Username "undefined" is already taken"
fakebackend interceptor error message: "Username "undefined" is already taken"
我正在关注此 tutorial 以实现一个简单的 signup/login 功能,其中包含一个假后端拦截器。到目前为止,我刚刚构建了注册组件、用户模型和服务,以及与假 API 匹配的相应端点。但是当我将凭据发送到服务器时,我收到此错误:error: {message: "Username "undefined" is already taken"}
好像根本没有发送任何凭据...
假后端:
...The imports...
// array in local storage for registered users
let users = JSON.parse(localStorage.getItem('users')) || [];
@Injectable()
export class FakeBackendInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const { url, method, headers, body } = request;
// wrap in delayed observable to simulate server api call
return of(null)
.pipe(mergeMap(handleRoute))
.pipe(materialize()) // call materialize and dematerialize to ensure delay even if an error is thrown (https://github.com/Reactive-Extensions/RxJS/issues/648)
.pipe(delay(500))
.pipe(dematerialize());
function handleRoute() {
switch (true) {
case url.endsWith('/users/register') && method === 'POST':
return register();
case url.endsWith('/users/authenticate') && method === 'POST':
return authenticate();
case url.endsWith('/users') && method === 'GET':
return getUsers();
case url.match(/\/users\/\d+$/) && method === 'GET':
return getUserById();
case url.match(/\/users\/\d+$/) && method === 'DELETE':
return deleteUser();
default:
// pass through any requests not handled above
return next.handle(request);
}
}
// route functions
function register() {
const user = body
if (users.find(x => x.username === user.username)) {
return error('Username "' + user.username + '" is already taken')
}
user.id = users.length ? Math.max(...users.map(x => x.id)) + 1 : 1;
users.push(user);
localStorage.setItem('users', JSON.stringify(users));
return ok();
}
function authenticate() {
const { username, password } = body;
const user = users.find(x => x.username === username && x.password === password);
if (!user) return error('Username or password is incorrect');
return ok({
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
token: 'fake-jwt-token'
})
}
function getUsers() {
if (!isLoggedIn()) return unauthorized();
return ok(users);
}
function getUserById() {
if (!isLoggedIn()) return unauthorized();
const user = users.find(x => x.id == idFromUrl());
return ok(user);
}
function deleteUser() {
if (!isLoggedIn()) return unauthorized();
users = users.filter(x => x.id !== idFromUrl());
localStorage.setItem('users', JSON.stringify(users));
return ok();
}
// helper functions
function ok(body?) {
return of(new HttpResponse({ status: 200, body }))
}
function unauthorized() {
return throwError({ status: 401, error: { message: 'Unauthorised' } });
}
function error(message) {
return throwError({ error: { message } });
}
function isLoggedIn() {
return headers.get('Authorization') === 'Bearer fake-jwt-token';
}
function idFromUrl() {
const urlParts = url.split('/');
return parseInt(urlParts[urlParts.length - 1]);
}
}
}
export const fakeBackendProvider = {
// use fake backend in place of Http service for backend-less development
provide: HTTP_INTERCEPTORS,
useClass: FakeBackendInterceptor,
multi: true
};
注册组件:
ngOnInit() {
this.registerForm = this.fb.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
userName: ['', Validators.required],
password: [null,[ Validators.required, Validators.minLength(6)]]
})
}
get firstName() { return this.registerForm.get('firstName') }
get lastName() { return this.registerForm.get('lastName') }
get userName() { return this.registerForm.get('userName') }
get password() { return this.registerForm.get('password') }
onSubmit() {
if (this.registerForm.invalid) return;
this.userService.register(this.registerForm.value).subscribe(
data => {
console.log(data)
},
error => {
console.log(error)
});
}
和服务:
constructor(private http: HttpClient) { }
register(credentials){
console.log(credentials)
return this.http.post(`/users/register`, credentials)
}
这是控制台的快照
我还没有实现所有功能,例如消息服务,但我的问题是,为什么我会从后端收到此响应?我不应该获得 200 状态,因为注册功能设置为这样做吗?注意:app.module 已正确设置,因为 fakebackend 已注册。此外,如果我尝试使用此设置获取用户,我会得到一个 401,这是正确的,因为我没有登录,因此拦截器正在工作。有人可以帮我吗?
在您的表单中,您正在使用 userName
,而在拦截器中,您正在检查 username
。如果你要在浏览器中清除你的 localStorage,你可能会注意到它第一次工作!为什么...?
好的,第一次你的数组是空的:
let users = JSON.parse(localStorage.getItem('users')) || [];
因此,如果您键入用户名 test
,您的有效负载将类似于...
{ userName: 'test' }
注意大写 N
..所以这通过了:
if (users.find(x => x.username === user.username)) {
return error('Username "' + user.username + '" is already taken');
}
因为数组最初是空的。
然后你这样做:
user.id = users.length ? Math.max(...users.map(x => x.id)) + 1 : 1;
users.push(user);
localStorage.setItem("users", JSON.stringify(users));
所以您现在存储在 users
中的是
[{userName: 'test'}]
现在第二次不及格了!由于 find
现在将 undefined
与 undefined
进行比较,因为 username
属性 不存在于您在 find
中比较的任何一个对象中。 .. undefined === undefined
是真实的,因此它将 return 您的 users
数组中的第一项。
将您的比较更改为:
if (users.find(x => x.userName === user.userName))
它会正常工作。 Oooooor 将表单控件名称更改为 username
...
我正在关注此 tutorial 以实现一个简单的 signup/login 功能,其中包含一个假后端拦截器。到目前为止,我刚刚构建了注册组件、用户模型和服务,以及与假 API 匹配的相应端点。但是当我将凭据发送到服务器时,我收到此错误:error: {message: "Username "undefined" is already taken"}
好像根本没有发送任何凭据...
假后端:
...The imports...
// array in local storage for registered users
let users = JSON.parse(localStorage.getItem('users')) || [];
@Injectable()
export class FakeBackendInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const { url, method, headers, body } = request;
// wrap in delayed observable to simulate server api call
return of(null)
.pipe(mergeMap(handleRoute))
.pipe(materialize()) // call materialize and dematerialize to ensure delay even if an error is thrown (https://github.com/Reactive-Extensions/RxJS/issues/648)
.pipe(delay(500))
.pipe(dematerialize());
function handleRoute() {
switch (true) {
case url.endsWith('/users/register') && method === 'POST':
return register();
case url.endsWith('/users/authenticate') && method === 'POST':
return authenticate();
case url.endsWith('/users') && method === 'GET':
return getUsers();
case url.match(/\/users\/\d+$/) && method === 'GET':
return getUserById();
case url.match(/\/users\/\d+$/) && method === 'DELETE':
return deleteUser();
default:
// pass through any requests not handled above
return next.handle(request);
}
}
// route functions
function register() {
const user = body
if (users.find(x => x.username === user.username)) {
return error('Username "' + user.username + '" is already taken')
}
user.id = users.length ? Math.max(...users.map(x => x.id)) + 1 : 1;
users.push(user);
localStorage.setItem('users', JSON.stringify(users));
return ok();
}
function authenticate() {
const { username, password } = body;
const user = users.find(x => x.username === username && x.password === password);
if (!user) return error('Username or password is incorrect');
return ok({
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
token: 'fake-jwt-token'
})
}
function getUsers() {
if (!isLoggedIn()) return unauthorized();
return ok(users);
}
function getUserById() {
if (!isLoggedIn()) return unauthorized();
const user = users.find(x => x.id == idFromUrl());
return ok(user);
}
function deleteUser() {
if (!isLoggedIn()) return unauthorized();
users = users.filter(x => x.id !== idFromUrl());
localStorage.setItem('users', JSON.stringify(users));
return ok();
}
// helper functions
function ok(body?) {
return of(new HttpResponse({ status: 200, body }))
}
function unauthorized() {
return throwError({ status: 401, error: { message: 'Unauthorised' } });
}
function error(message) {
return throwError({ error: { message } });
}
function isLoggedIn() {
return headers.get('Authorization') === 'Bearer fake-jwt-token';
}
function idFromUrl() {
const urlParts = url.split('/');
return parseInt(urlParts[urlParts.length - 1]);
}
}
}
export const fakeBackendProvider = {
// use fake backend in place of Http service for backend-less development
provide: HTTP_INTERCEPTORS,
useClass: FakeBackendInterceptor,
multi: true
};
注册组件:
ngOnInit() {
this.registerForm = this.fb.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
userName: ['', Validators.required],
password: [null,[ Validators.required, Validators.minLength(6)]]
})
}
get firstName() { return this.registerForm.get('firstName') }
get lastName() { return this.registerForm.get('lastName') }
get userName() { return this.registerForm.get('userName') }
get password() { return this.registerForm.get('password') }
onSubmit() {
if (this.registerForm.invalid) return;
this.userService.register(this.registerForm.value).subscribe(
data => {
console.log(data)
},
error => {
console.log(error)
});
}
和服务:
constructor(private http: HttpClient) { }
register(credentials){
console.log(credentials)
return this.http.post(`/users/register`, credentials)
}
这是控制台的快照
我还没有实现所有功能,例如消息服务,但我的问题是,为什么我会从后端收到此响应?我不应该获得 200 状态,因为注册功能设置为这样做吗?注意:app.module 已正确设置,因为 fakebackend 已注册。此外,如果我尝试使用此设置获取用户,我会得到一个 401,这是正确的,因为我没有登录,因此拦截器正在工作。有人可以帮我吗?
在您的表单中,您正在使用 userName
,而在拦截器中,您正在检查 username
。如果你要在浏览器中清除你的 localStorage,你可能会注意到它第一次工作!为什么...?
好的,第一次你的数组是空的:
let users = JSON.parse(localStorage.getItem('users')) || [];
因此,如果您键入用户名 test
,您的有效负载将类似于...
{ userName: 'test' }
注意大写 N
..所以这通过了:
if (users.find(x => x.username === user.username)) {
return error('Username "' + user.username + '" is already taken');
}
因为数组最初是空的。
然后你这样做:
user.id = users.length ? Math.max(...users.map(x => x.id)) + 1 : 1;
users.push(user);
localStorage.setItem("users", JSON.stringify(users));
所以您现在存储在 users
中的是
[{userName: 'test'}]
现在第二次不及格了!由于 find
现在将 undefined
与 undefined
进行比较,因为 username
属性 不存在于您在 find
中比较的任何一个对象中。 .. undefined === undefined
是真实的,因此它将 return 您的 users
数组中的第一项。
将您的比较更改为:
if (users.find(x => x.userName === user.userName))
它会正常工作。 Oooooor 将表单控件名称更改为 username
...