ts-client WebSockets + JSON
This commit is contained in:
parent
5e3be6e212
commit
6859d33536
|
|
@ -0,0 +1 @@
|
|||
js/
|
||||
|
|
@ -3,7 +3,55 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title></title>
|
||||
<script src="js/signalr-client.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
let connectButton = document.getElementById('connect');
|
||||
let connection = new RpcConnection(`http://${document.location.host}/hubs`);
|
||||
connection.on('Send',(msg) => { addLine(msg); });
|
||||
let isConnected = false;
|
||||
|
||||
connectButton.addEventListener('click', () => {
|
||||
connection.start()
|
||||
.then(() => {
|
||||
isConnected = true;
|
||||
})
|
||||
.catch(err => {
|
||||
addLine(err, true);
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('sendmessage').addEventListener('submit', event => {
|
||||
let data = document.getElementById('data').value;
|
||||
|
||||
if (isConnected) {
|
||||
connection.invoke('SocketsSample.Hubs.Chat.Send', data)
|
||||
.then(result => {
|
||||
console.log("invocation completed successfully: " + (result === null ? '(null)' : result));
|
||||
|
||||
if (result != null) {
|
||||
addLine(result);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
addLine(err, true);
|
||||
});
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
});
|
||||
|
||||
function addLine(line, isError) {
|
||||
var child = document.createElement('li');
|
||||
if (isError === true) {
|
||||
child.style.color = 'red';
|
||||
}
|
||||
child.innerText = line;
|
||||
document.getElementById('messages').appendChild(child);
|
||||
}
|
||||
|
||||
/*
|
||||
function hubConnection(url) {
|
||||
var ws = new WebSocket(url);
|
||||
var id = 0;
|
||||
|
|
@ -108,7 +156,7 @@
|
|||
event.preventDefault();
|
||||
});
|
||||
};
|
||||
|
||||
*/
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
class HttpClient {
|
||||
get(url: string): Promise<string> {
|
||||
return this.xhr("GET", url);
|
||||
}
|
||||
|
||||
post(url: string): Promise<string> {
|
||||
return this.xhr("POST", url);
|
||||
}
|
||||
|
||||
private xhr(method: string, url: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open(method, url, true);
|
||||
xhr.send();
|
||||
xhr.onload = () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
resolve(xhr.response);
|
||||
}
|
||||
else {
|
||||
reject({
|
||||
status: xhr.status,
|
||||
statusText: xhr.statusText
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = () => {
|
||||
reject({
|
||||
status: xhr.status,
|
||||
statusText: xhr.statusText
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
interface ITransport {
|
||||
connect(url: string, queryString: string): Promise<void>;
|
||||
send(data: string): Promise<void>;
|
||||
stop(): void;
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
interface InvocationDescriptor {
|
||||
readonly Id: string;
|
||||
readonly Method: string;
|
||||
readonly Arguments: Array<any>;
|
||||
}
|
||||
|
||||
interface InvocationResultDescriptor {
|
||||
readonly Id: string;
|
||||
readonly Error: string;
|
||||
readonly Result: any;
|
||||
}
|
||||
|
||||
class RpcConnection {
|
||||
// TODO: add connection state
|
||||
private url: string;
|
||||
private queryString: string;
|
||||
private callbacks: Map<string, (any) => void>;
|
||||
private methods: Map<string, (...args:any[]) => void>;
|
||||
private transport: ITransport;
|
||||
private id: number;
|
||||
|
||||
constructor(url: string, queryString: string) {
|
||||
this.url = url;
|
||||
this.queryString = queryString;
|
||||
this.callbacks = new Map<string, (any) => void>();
|
||||
this.methods = new Map<string, (...args:any[]) => void>();
|
||||
this.id = 0;
|
||||
}
|
||||
|
||||
private messageReceived(data: string) {
|
||||
//TODO: separate JSON parsing
|
||||
var descriptor = JSON.parse(data);
|
||||
if (descriptor.Method === undefined) {
|
||||
let invocationResult: InvocationResultDescriptor = descriptor;
|
||||
let callback = this.callbacks[invocationResult.Id];
|
||||
if (callback != null) {
|
||||
callback(invocationResult);
|
||||
this.callbacks.delete(invocationResult.Id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
let invocation: InvocationDescriptor = descriptor;
|
||||
let method = this.methods[invocation.Method];
|
||||
if (method != null) {
|
||||
// TODO: bind? args?
|
||||
method.apply(this, invocation.Arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
new HttpClient().get(this.url + "/getid?" + this.queryString)
|
||||
.then(id => {
|
||||
this.transport = new WebSocketTransport(data => this.messageReceived(data));
|
||||
return this.transport.connect(this.url, `id=${id}`);
|
||||
})
|
||||
.then(() => {
|
||||
resolve();
|
||||
})
|
||||
.catch(() => {
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
this.transport.stop();
|
||||
}
|
||||
|
||||
invoke(methodName: string, ...args: any[]): Promise<void> {
|
||||
let invocationDescriptor: InvocationDescriptor = {
|
||||
"Id": this.id.toString(),
|
||||
"Method": methodName,
|
||||
"Arguments": args
|
||||
};
|
||||
|
||||
let p = new Promise<any>((resolve, reject) => {
|
||||
this.callbacks[this.id] = (invocationResult: InvocationResultDescriptor) => {
|
||||
if (invocationResult.Error != null) {
|
||||
reject(invocationResult.Error);
|
||||
}
|
||||
else {
|
||||
resolve(invocationResult.Result);
|
||||
}
|
||||
};
|
||||
|
||||
this.transport.send(JSON.stringify(invocationDescriptor))
|
||||
.catch(e => reject(e));
|
||||
});
|
||||
|
||||
this.id++;
|
||||
|
||||
//TODO: separate conversion to enable different data formats
|
||||
return p;
|
||||
}
|
||||
|
||||
on(methodName: string, method: (...args: any[]) => void) {
|
||||
this.methods[methodName] = method;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
class WebSocketTransport implements ITransport {
|
||||
private webSocket: WebSocket;
|
||||
// TODO: make the callback a named type
|
||||
// TODO: string won't work for binary formats
|
||||
private receiveCallback: (data: string) => void;
|
||||
|
||||
constructor(receiveCallback: (data: string) => void) {
|
||||
this.receiveCallback = receiveCallback;
|
||||
}
|
||||
|
||||
connect(url: string, queryString: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
url = url.replace(/^http/, "ws");
|
||||
let connectUrl = url + "/ws?" + queryString;
|
||||
this.webSocket = new WebSocket(connectUrl);
|
||||
this.webSocket.onopen = (event: Event) => {
|
||||
console.log(`WebSocket connected to ${connectUrl}`);
|
||||
resolve();
|
||||
};
|
||||
|
||||
this.webSocket.onerror = (event: Event) => {
|
||||
// TODO: handle when connection was opened successfully
|
||||
reject();
|
||||
};
|
||||
|
||||
this.webSocket.onmessage = (message: MessageEvent) => {
|
||||
this.receiveCallback(message.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
send(data: string): Promise<void> {
|
||||
this.webSocket.send(data);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
this.webSocket.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"out": "../../samples/SocketsSample/wwwroot/js/signalr-client.js"
|
||||
},
|
||||
"include": [ "*.ts" ]
|
||||
}
|
||||
Loading…
Reference in New Issue