SignalR 之类的功能不起作用
SignalR like functionality is not working
我创建了一个点赞功能,这样用户就可以在我的应用程序中点赞 post。我读过 SignalR 并尝试使用它,以便每当用户 likes/unlikes 和 post 时,喜欢的数量可以实时自动更新。但是,它不起作用,但我也没有收到任何错误。按下赞按钮后我控制台中的唯一消息是:
Information: WebSocket connected to wss://localhost:44351/hubs/like?access_token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiIxIiwidW5pcXVlX25hbWUiOiJnZW9yZ2lhIiwicm9sZSI6WyJNZW1iZXIiLCJBZG1pbiJdLCJuYmYiOjE2MTk0NjQ3NzAsImV4cCI6MTYyMDA2OTU3MCwiaWF0IjoxNjE5NDY0NzcwfQ.1Bwf_Y2QJP_VjRUXaBeqz5sueV6oTIpVlOLU4kOEmLf2Y_hfxJbc5_f4yksY9R45YGz0qPWw-rc10I7pobFJYQ
这是我的 .net 代码:
public class LikeHub : Hub
{
private readonly IPostRepository _postRepository;
private readonly DataContext _context;
private readonly IUserRepository _userRepository;
public LikeHub(IPostRepository postRepository, DataContext context, IUserRepository userRepository)
{
_postRepository = postRepository;
_context = context;
_userRepository = userRepository;
}
public async Task SetLike(int userId, int postId)
{
Like l = new Like();
Like temp = _context.Likes.Where(x => x.PostId == postId && x.UserId == userId).FirstOrDefault();
if(temp != null)
{
_context.Likes.Remove(temp);
} else
{
_context.Likes.Add(l);
l.UserId = userId;
l.PostId = postId;
}
await _context.SaveChangesAsync();
int numOfLikes = _context.Likes.Where(x => x.PostId == postId).Count();
await Clients.All.SendAsync("ReceiveMessage", numOfLikes, postId, userId);
}
}
这是我在 PostsService 中的 Angular 代码:
export class PostsService {
hubUrl = environment.hubUrl;
private hubConnection: HubConnection;
likeMessageReceive: EventEmitter<{ numOfLikes: number, postId: number, userId: number }> = new EventEmitter<{ numOfLikes:number, postId: number, userId: number }>();
constructor(private http: HttpClient) {}
connectHubs(user: User) {
this.hubConnection = new HubConnectionBuilder()
.withUrl(this.hubUrl + 'like', { accessTokenFactory: () => user.token,
skipNegotiation: true, transport: signalR.HttpTransportType.WebSockets })
.build();
return this.hubConnection.start()
.then(() => {
this.hubConnection.on('ReceiveMessage', (numOfLikes, postId, userId) => {
this.likeMessageReceive.emit({ numOfLikes, postId, userId });
});
})
.catch(error => console.log(error));
}
setLike(userId: number, postId: number) {
this.hubConnection.invoke('SetLike', userId, postId);
}
closeHubConnections() {
this.hubConnection.stop();
}
}
这是我的 PostCardComponent 中的 Angular 代码,其中点赞按钮是:
export class PostCardComponent implements OnInit {
@Input() post: Post;
likesSubscription: Subscription;
constructor(private postService:PostsService,public accountService:AccountService)
{ this.Login$ = this.accountService.Logged;}
ngOnInit(): void {
this.likesSubscription = this.postService.likeMessageReceive.subscribe(result =>{
if (result.postId === this.post.id) {
this.post.likes.length = result.numOfLikes;
}
})
}
liked(post: Post) {
const user: User = JSON.parse(localStorage.getItem('user'));
this.postService.setLike(user.id, post.id);
}
}
这是 PostListComponent,其中所有 post 是:
export class PostListComponent implements OnInit {
posts: Post[];
post: Post;
likesSubscription: Subscription;
localUser: User;
constructor(private postService: PostsService) {}
ngOnInit(): void {
this.postService.connectHubs(this.localUser);
}
}
不知道this.hubConnection.on()
中的代码是否正确,或者给的参数是否正确。我还在 Startup.cs class.
的端点中添加了 LikeHub
我强烈建议从仔细重写这个例子开始,这应该真的有助于更好地理解概念https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr?view=aspnetcore-5.0&tabs=visual-studio
因此,此代码中存在几个问题。 PostsService 的 createLike 方法应该只负责通过现有连接的 post 调用。直到此时,负责连接启动的所有其他代码都应该已经执行。
https://docs.microsoft.com/en-us/aspnet/core/signalr/javascript-client?view=aspnetcore-5.0#connect-to-a-hub
因此,如果您不熟悉响应式编程和 rxjs,我建议您向 PostsService 添加一些方法,例如 ConnectHubs():承诺在实际调用一些方法之前准备您的集线器连接集线器方法。
connectHubs() {
this.hubConnection = new HubConnectionBuilder()
.withUrl(this.hubUrl + 'like', { accessTokenFactory: () => user.token,
skipNegotiation: true, transport: signalR.HttpTransportType.WebSockets })
.build();
return this.hubConnection.start()
.then(() => {
this.hubConnection.on('ReceiveMessage', (numOfLikes, postId, userId) => {
// some logic to handle invocation
});
})
.catch(error => console.log(error));
}
setLike(userId: number, postId: number) {
this.hubConnection.invoke('SetLike', userId, postId);
}
closeHubConnections() {
this.hubConnection.stop();
}
然后在包含多个 post 的组件中,除了从 api 请求所有 post 之外,您还需要调用此 connectHubs 方法并等待此承诺显示所有 posts,以避免在可能之前设置喜欢。在这种情况下,最好也停止 ngOnDestroy 中的连接,以避免从一个客户端到同一集线器的不必要的多个活动连接。或者你可以在你非常基础的组件中调用这个 init 方法,比如应用程序组件,在这种情况下你不需要在 ngOnDestroy 中停止连接,但是你需要确保你的用户在连接建立之前已经登录。也许你可以找到一些组件,它在极少数情况下会被破坏,但它总是会在登录后打开
如果你了解 rxjs,你可以添加一些 BehaviorSubject 字段,比如
private isConnectedSubject = new BehaviorSubject(false);
isConnected$ = isConnectedSubject.asObservable();
然后您可以添加类似 isConnectedSubject.next(true) 的内容,而不是在连接开始时返回承诺;
在您的断开连接方法中,您可以添加 isConnectedSubject.next(false);
在您的组件中,您可以在集线器未以这种方式连接时禁用类似按钮:
<button [disabled]="!(postService.isConnected$ | async)" ...>
为了让您的控件了解此中心的变化,如果您了解 RxJS,则可以在每次收到新消息时添加一些带有 Observable 字段的 Subject 字段和 post 事件。或者您可以使用事件发射器 https://angular.io/api/core/EventEmitter 使其更简单,如下所示
服务:
likeMessageReceive = new EventEmitter<{ numOfLikes, postId, userId }>();
connectHubs() {
....
this.hubConnection.on('ReceiveMessage', (numOfLikes, postId, userId) => {
likeMessageReceive.emit({ numOfLikes, postId, userId })
console.log(numOfLikes);
})
....
Post分量:
likesSubscription: Subscription;
ngOnInit() {
this.likesSubscription = this.postsService.likeMessageReceive.subscribe(result =>{
if (result.postId === this.post.id) {
this.post.likes.length = numOfLikes;
}
})
}
liked(post: Post) {
const user: User = JSON.parse(localStorage.getItem('user'));
this.postService.setLike(user.id, post.id);
}
ngOnDestroy() {
if (this.likesSubscription) {
this.likesSubscription.unsubscribe();
}
}
使用 rxjs 会非常相似,但您将使用 Subject 而不是发射器,不要忘记取消订阅以避免意外行为和泄漏。
我创建了一个点赞功能,这样用户就可以在我的应用程序中点赞 post。我读过 SignalR 并尝试使用它,以便每当用户 likes/unlikes 和 post 时,喜欢的数量可以实时自动更新。但是,它不起作用,但我也没有收到任何错误。按下赞按钮后我控制台中的唯一消息是:
Information: WebSocket connected to wss://localhost:44351/hubs/like?access_token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiIxIiwidW5pcXVlX25hbWUiOiJnZW9yZ2lhIiwicm9sZSI6WyJNZW1iZXIiLCJBZG1pbiJdLCJuYmYiOjE2MTk0NjQ3NzAsImV4cCI6MTYyMDA2OTU3MCwiaWF0IjoxNjE5NDY0NzcwfQ.1Bwf_Y2QJP_VjRUXaBeqz5sueV6oTIpVlOLU4kOEmLf2Y_hfxJbc5_f4yksY9R45YGz0qPWw-rc10I7pobFJYQ
这是我的 .net 代码:
public class LikeHub : Hub
{
private readonly IPostRepository _postRepository;
private readonly DataContext _context;
private readonly IUserRepository _userRepository;
public LikeHub(IPostRepository postRepository, DataContext context, IUserRepository userRepository)
{
_postRepository = postRepository;
_context = context;
_userRepository = userRepository;
}
public async Task SetLike(int userId, int postId)
{
Like l = new Like();
Like temp = _context.Likes.Where(x => x.PostId == postId && x.UserId == userId).FirstOrDefault();
if(temp != null)
{
_context.Likes.Remove(temp);
} else
{
_context.Likes.Add(l);
l.UserId = userId;
l.PostId = postId;
}
await _context.SaveChangesAsync();
int numOfLikes = _context.Likes.Where(x => x.PostId == postId).Count();
await Clients.All.SendAsync("ReceiveMessage", numOfLikes, postId, userId);
}
}
这是我在 PostsService 中的 Angular 代码:
export class PostsService {
hubUrl = environment.hubUrl;
private hubConnection: HubConnection;
likeMessageReceive: EventEmitter<{ numOfLikes: number, postId: number, userId: number }> = new EventEmitter<{ numOfLikes:number, postId: number, userId: number }>();
constructor(private http: HttpClient) {}
connectHubs(user: User) {
this.hubConnection = new HubConnectionBuilder()
.withUrl(this.hubUrl + 'like', { accessTokenFactory: () => user.token,
skipNegotiation: true, transport: signalR.HttpTransportType.WebSockets })
.build();
return this.hubConnection.start()
.then(() => {
this.hubConnection.on('ReceiveMessage', (numOfLikes, postId, userId) => {
this.likeMessageReceive.emit({ numOfLikes, postId, userId });
});
})
.catch(error => console.log(error));
}
setLike(userId: number, postId: number) {
this.hubConnection.invoke('SetLike', userId, postId);
}
closeHubConnections() {
this.hubConnection.stop();
}
}
这是我的 PostCardComponent 中的 Angular 代码,其中点赞按钮是:
export class PostCardComponent implements OnInit {
@Input() post: Post;
likesSubscription: Subscription;
constructor(private postService:PostsService,public accountService:AccountService)
{ this.Login$ = this.accountService.Logged;}
ngOnInit(): void {
this.likesSubscription = this.postService.likeMessageReceive.subscribe(result =>{
if (result.postId === this.post.id) {
this.post.likes.length = result.numOfLikes;
}
})
}
liked(post: Post) {
const user: User = JSON.parse(localStorage.getItem('user'));
this.postService.setLike(user.id, post.id);
}
}
这是 PostListComponent,其中所有 post 是:
export class PostListComponent implements OnInit {
posts: Post[];
post: Post;
likesSubscription: Subscription;
localUser: User;
constructor(private postService: PostsService) {}
ngOnInit(): void {
this.postService.connectHubs(this.localUser);
}
}
不知道this.hubConnection.on()
中的代码是否正确,或者给的参数是否正确。我还在 Startup.cs class.
我强烈建议从仔细重写这个例子开始,这应该真的有助于更好地理解概念https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr?view=aspnetcore-5.0&tabs=visual-studio
因此,此代码中存在几个问题。 PostsService 的 createLike 方法应该只负责通过现有连接的 post 调用。直到此时,负责连接启动的所有其他代码都应该已经执行。 https://docs.microsoft.com/en-us/aspnet/core/signalr/javascript-client?view=aspnetcore-5.0#connect-to-a-hub
因此,如果您不熟悉响应式编程和 rxjs,我建议您向 PostsService 添加一些方法,例如 ConnectHubs():承诺在实际调用一些方法之前准备您的集线器连接集线器方法。
connectHubs() {
this.hubConnection = new HubConnectionBuilder()
.withUrl(this.hubUrl + 'like', { accessTokenFactory: () => user.token,
skipNegotiation: true, transport: signalR.HttpTransportType.WebSockets })
.build();
return this.hubConnection.start()
.then(() => {
this.hubConnection.on('ReceiveMessage', (numOfLikes, postId, userId) => {
// some logic to handle invocation
});
})
.catch(error => console.log(error));
}
setLike(userId: number, postId: number) {
this.hubConnection.invoke('SetLike', userId, postId);
}
closeHubConnections() {
this.hubConnection.stop();
}
然后在包含多个 post 的组件中,除了从 api 请求所有 post 之外,您还需要调用此 connectHubs 方法并等待此承诺显示所有 posts,以避免在可能之前设置喜欢。在这种情况下,最好也停止 ngOnDestroy 中的连接,以避免从一个客户端到同一集线器的不必要的多个活动连接。或者你可以在你非常基础的组件中调用这个 init 方法,比如应用程序组件,在这种情况下你不需要在 ngOnDestroy 中停止连接,但是你需要确保你的用户在连接建立之前已经登录。也许你可以找到一些组件,它在极少数情况下会被破坏,但它总是会在登录后打开
如果你了解 rxjs,你可以添加一些 BehaviorSubject 字段,比如
private isConnectedSubject = new BehaviorSubject(false);
isConnected$ = isConnectedSubject.asObservable();
然后您可以添加类似 isConnectedSubject.next(true) 的内容,而不是在连接开始时返回承诺; 在您的断开连接方法中,您可以添加 isConnectedSubject.next(false); 在您的组件中,您可以在集线器未以这种方式连接时禁用类似按钮:
<button [disabled]="!(postService.isConnected$ | async)" ...>
为了让您的控件了解此中心的变化,如果您了解 RxJS,则可以在每次收到新消息时添加一些带有 Observable 字段的 Subject 字段和 post 事件。或者您可以使用事件发射器 https://angular.io/api/core/EventEmitter 使其更简单,如下所示
服务:
likeMessageReceive = new EventEmitter<{ numOfLikes, postId, userId }>();
connectHubs() {
....
this.hubConnection.on('ReceiveMessage', (numOfLikes, postId, userId) => {
likeMessageReceive.emit({ numOfLikes, postId, userId })
console.log(numOfLikes);
})
....
Post分量:
likesSubscription: Subscription;
ngOnInit() {
this.likesSubscription = this.postsService.likeMessageReceive.subscribe(result =>{
if (result.postId === this.post.id) {
this.post.likes.length = numOfLikes;
}
})
}
liked(post: Post) {
const user: User = JSON.parse(localStorage.getItem('user'));
this.postService.setLike(user.id, post.id);
}
ngOnDestroy() {
if (this.likesSubscription) {
this.likesSubscription.unsubscribe();
}
}
使用 rxjs 会非常相似,但您将使用 Subject 而不是发射器,不要忘记取消订阅以避免意外行为和泄漏。