ts-client WebSockets + JSON
This commit is contained in:
parent
5e3be6e212
commit
6859d33536
|
|
@ -0,0 +1 @@
|
||||||
|
js/
|
||||||
|
|
@ -3,7 +3,55 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title></title>
|
<title></title>
|
||||||
|
<script src="js/signalr-client.js"></script>
|
||||||
<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) {
|
function hubConnection(url) {
|
||||||
var ws = new WebSocket(url);
|
var ws = new WebSocket(url);
|
||||||
var id = 0;
|
var id = 0;
|
||||||
|
|
@ -108,7 +156,7 @@
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<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