dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.43k stars 10.02k forks source link

`No client method with the name found` warning when running application in the Development environment #57881

Closed Manojkumar226 closed 1 month ago

Manojkumar226 commented 1 month ago

Is there an existing issue for this?

Describe the bug

Context: For real-time updates in the react web application, we have adopted the signalR library in the frontend web application and Azure signalR service at the backend.

Problem: We are observing the No client method with the name found warning messages in the console when we run the react application in development environment(http://localhost:4200/). However, the signalR events from the backend are working successfully without any warnings in the production environment (for example: https://test.com).

Note: For the signalR at the azure we have configured the CORS with * allowing the signalR to be working with any external hosts image

Expected Behavior

Receiving signalr events successfully even when we are running the web application in the development environment(localhost)

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

No response

Anything else?

No response

BrennanConroy commented 1 month ago

You probably didn't call hubConnection.On before hubConnection.Start. The issue as-is isn't actionable from us.

Manojkumar226 commented 1 month ago

You probably didn't call hubConnection.On before hubConnection.Start. The issue as-is isn't actionable from us.

@BrennanConroy,

Firstly, we are establish the signaR connection with the customHook at the top level in the application and then we initializing the another custom hook i.e, event handler which is listing to the events from the connection.On whereever the realtime updates are needed in the application.

As the both the environments(production and development) have the same signalR implementation, we don't understand why we are facing the warning in only the development environment. Please let me know how we can change the existing implementation.

SignalR Initalization:

const useSignalRInitialization = (
  props: UseSignalRInitializationProps
): HookReturnTypes => {
  const [signalRResponse, setSignalRResponse] = useState<SignalRResponseType>(
    props.signalRResponse
  )
  const getNewAccessTokenAPI = useGetSignalRAccessToken()
  const retryTimeoutRef = useRef<NodeJS.Timeout | null>(null)

  const signalRConnection = new HubConnectionBuilder()
    .withUrl(signalRResponse.url, {
      accessTokenFactory: () => signalRResponse.accessToken,
      skipNegotiation: true,
      transport: HttpTransportType.WebSockets
    })
    .configureLogging(LogLevel.Debug)
    .withKeepAliveInterval(15000)
    .withServerTimeout(30000)
    .build()

  const startConnection = async (): Promise<void> => {
    if (signalRConnection.state !== HubConnectionState.Disconnected) {
      console.log(
        'Connection is not in the Disconnected state. Current state:',
        signalRConnection.state
      )
      return
    }

    try {
      await signalRConnection.start()
      console.log('Connected to SignalR')
    } catch (err) {
      console.error('Error connecting to SignalR:', err)
      await refreshAccessToken()

      retryTimeoutRef.current = setTimeout(() => startConnection(), 5000)
    }
  }

  const checkTokenExpiry = (): boolean => {
    const currentTime = getCurrentTime()
    const expiryDateAndTime = signalRResponse.expiresAt

    const accessTokenExpiryTime =
      getDateObjectFromDateString(expiryDateAndTime).getTime()

    return currentTime > accessTokenExpiryTime
  }

  const refreshAccessToken = async (): Promise<void> => {
    const isTokenExpired = checkTokenExpiry()
    if (isTokenExpired) {
      try {
        await getNewAccessTokenAPI.triggerAPI({
          deviceId: getDeviceId(),
          onSuccess: setSignalRResponse
        })
      } catch (error) {
        console.error('Failed to refresh access token:', error)
      }
    }
  }

  const stopConnection = async (): Promise<void> => {
    if (
      signalRConnection.state === HubConnectionState.Connected ||
      signalRConnection.state === HubConnectionState.Connecting
    ) {
      console.log('Stopping SignalR connection')
      await signalRConnection.stop()
    }
  }

  useEffect(() => {
    startConnection()

    signalRConnection.onclose(async () => {
      console.log('trying to reconnect to signalR server')

      refreshAccessToken()
    })

    const refreshInterval = setInterval(() => {
      refreshAccessToken()
    }, 5 * 60 * 1000) // Refresh token every 5 minutes

    return () => {
      clearInterval(refreshInterval)
      if (retryTimeoutRef.current) {
        clearTimeout(retryTimeoutRef.current)
      }
    }
  }, [signalRResponse.accessToken])

  return {
    signalRHubConnection: signalRConnection
  }
}

export default useSignalRInitialization

EventHandler:

const useSignalREventHandler = (
  eventName: string,
  callback: (message: any) => void
) => {
  const { signalRConnection } = useSignalRConnectionContext()

  useEffect(() => {
    if (!signalRConnection) return

    const handleEvent = (message: any): void => {
      console.log('Data Received from SignalR:', message)
      callback(message)
    }

    signalRConnection.on(eventName, handleEvent)

    return () => {
      signalRConnection.off(eventName, handleEvent)
    }
  }, [signalRConnection, eventName, callback])
}

export default useSignalREventHandler