65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET Core 2.0 使用 SignalR 技术

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.65/5 (9投票s)

2017年9月28日

CPOL

2分钟阅读

viewsIcon

21133

downloadIcon

315

ASP.NET Core 1.x.x 版本并未包含 SignalR 技术及其开发计划。时间过得很快,微软发布了 .NET Core 2.0 Preview 2 的预览版本。

引言

ASP.NET Core 1.x.x 版本并未包含 SignalR 技术及其开发计划。时间过得很快,微软发布了 .NET Core 2.0 Preview 2 的预览版本,距离正式版不远了。上述内容也提到,在 ASP.NET Core 2.0 中,SignalR 将作为重要的组件与 MVC 等框架一起发布。其开发团队也兑现了承诺,使用 TypeScript 重写其 JavaScript 客户端,服务器端将接近 ASP.NET Core 的开发方式,例如将集成到 ASP.NET Core 依赖注入框架中。

构建环境

要在 ASP.NET Core 2.0 中使用 SignalR,首先需要引用 Microsoft.AspNetCore.SignalRMicrosoft.AspNetCore.SignalR.Http 这两个包。

目前,ASP.NET Core 2.0 和 SignalR 仍处于预览版本,因此 NuGet 无法找到 SignalR 包,如果想添加引用,我们需要去 MyGet 查找。

添加 NuGet 源

在程序根目录下,创建一个名为 NuGet.Config 的新文件,内容如下

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <packageSources>
        <clear/>
            <add key="aspnetcidev" 
             value="https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json"/>
            <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json"/>
    </packageSources>
</configuration>

编辑项目文件 csproj

添加对上述两个包的引用

<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.0-alpha1-final" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Http" Version="0.0.1-alpha" />

我在这里使用的是当前最高的版本,当然版本号可能会每天变化。最新的 SignalR 在创建项目时默认不兼容 .NET Core SDK 2.0 Preview 1,因此也需要修改这里修订后的版本号:Microsoft.AspNetCore.All 2.0.0-preview3-26040

当然,也可以使用 dotnet cli 添加包引用

dotnet add package Microsoft.AspNetCore.SignalR --version 1.0.1-alpha-final 
--source https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json

dotnet add package Microsoft.AspNetCore.SignalR.Http --version 0.0.1-alpha 
--source https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json

添加配置代码

我们需要在 Startup 类的 ConfigureServices 方法中添加以下代码

public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();
}

将以下代码添加到 Startup 类的 Configure 方法中

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseStaticFiles();
    app.UseSignalR(routes =>
    {
        routes.MapHub<Chat>("hubs");
    });
}

添加 HUB 类

public class Chat : Hub
{
    public override async Task OnConnectedAsync()
    {
        await Clients.All.InvokeAsync("Send", $"{Context.ConnectionId} joined");
    }

    public override async Task OnDisconnectedAsync(Exception ex)
    {
        await Clients.All.InvokeAsync("Send", $"{Context.ConnectionId} left");
    }

    public Task Send(string message)
    {
        return Clients.All.InvokeAsync("Send", $"{Context.ConnectionId}: {message}");
    }

    public Task SendToGroup(string groupName, string message)
    {
        return Clients.Group(groupName).InvokeAsync
                ("Send", $"{Context.ConnectionId}@{groupName}: {message}");
    }

    public async Task JoinGroup(string groupName)
    {
        await Groups.AddAsync(Context.ConnectionId, groupName);

        await Clients.Group(groupName).InvokeAsync
                ("Send", $"{Context.ConnectionId} joined {groupName}");
    }

    public async Task LeaveGroup(string groupName)
    {
        await Groups.RemoveAsync(Context.ConnectionId, groupName);

        await Clients.Group(groupName).InvokeAsync
                ("Send", $"{Context.ConnectionId} left {groupName}");
    }

    public Task Echo(string message)
    {
        return Clients.Client(Context.ConnectionId).InvokeAsync
               ("Send", $"{Context.ConnectionId}: {message}");
    }
}

客户端支持

wwwroot 目录下创建一个名为 chat.html 的 HTML 静态文件,内容如下

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <h1 id="head1"></h1>
    <div>
        <select id="formatType">
            <option value="json">json</option>
            <option value="line">line</option>
        </select>

        <input type="button" id="connect" value="Connect" />
        <input type="button" id="disconnect" value="Disconnect" />
    </div>


    <h4>To Everybody</h4>
    <form class="form-inline">
        <div class="input-append">
            <input type="text" id="message-text" 
            placeholder="Type a message, name or group" />
            <input type="button" id="broadcast" 
            class="btn" value="Broadcast" />
            <input type="button" id="broadcast-exceptme" 
             class="btn" value="Broadcast (All Except Me)" />
            <input type="button" id="join" 
            class="btn" value="Enter Name" />
            <input type="button" id="join-group" 
            class="btn" value="Join Group" />
            <input type="button" id="leave-group" 
            class="btn" value="Leave Group" />
        </div>
    </form>

    <h4>To Me</h4>
    <form class="form-inline">
        <div class="input-append">
            <input type="text" 
            id="me-message-text" placeholder="Type a message" />
            <input type="button" 
            id="send" class="btn" value="Send to me" />
        </div>
    </form>

    <h4>Private Message</h4>
    <form class="form-inline">
        <div class="input-prepend input-append">
            <input type="text" name="private-message" 
            id="private-message-text" 
             placeholder="Type a message" />
            <input type="text" name="user" 
            id="target" placeholder="Type a user or group name" />

            <input type="button" id="privatemsg" 
            class="btn" value="Send to user" />
            <input type="button" id="groupmsg" 
            class="btn" value="Send to group" />
        </div>
    </form>

    <ul id="message-list"></ul>
</body>
</html>
<script src="signalr-client.js"></script>
<script src="utils.js"></script>
<script>
var isConnected = false;
function invoke(connection, method, ...args) {
    if (!isConnected) {
        return;
    }
    var argsArray = Array.prototype.slice.call(arguments);
    connection.invoke.apply(connection, argsArray.slice(1))
            .then(result => {
                console.log("invocation completed successfully: " + 
                           (result === null ? '(null)' : result));

                if (result) {
                    addLine('message-list', result);
                }
            })
            .catch(err => {
                addLine('message-list', err, 'red');
            });
}

function getText(id) {
    return document.getElementById(id).value;
}

let transportType = signalR.TransportType[getParameterByName('transport')] || 
                    signalR.TransportType.WebSockets;

document.getElementById('head1').innerHTML = signalR.TransportType[transportType];

let connectButton = document.getElementById('connect');
let disconnectButton = document.getElementById('disconnect');
disconnectButton.disabled = true;
var connection;

click('connect', event => {
    connectButton.disabled = true;
    disconnectButton.disabled = false;
    let http = new signalR.HttpConnection(`http://${document.location.host}/hubs`, 
               { transport: transportType });
    connection = new signalR.HubConnection(http);
    connection.on('Send', msg => {
        addLine('message-list', msg);
    });

    connection.onClosed = e => {
        if (e) {
            addLine('message-list', 'Connection closed with error: ' + e, 'red');
        }
        else {
            addLine('message-list', 'Disconnected', 'green');
        }
    }

    connection.start()
        .then(() => {
            isConnected = true;
            addLine('message-list', 'Connected successfully', 'green');
        })
        .catch(err => {
            addLine('message-list', err, 'red');
        });
});

click('disconnect', event => {
    connectButton.disabled = false;
    disconnectButton.disabled = true;
    connection.stop()
        .then(() => {
            isConnected = false;
        });
});

click('broadcast', event => {
    let data = getText('message-text');
    invoke(connection, 'Send', data);
});

click('join-group', event => {
    let groupName = getText('message-text');
    invoke(connection, 'JoinGroup', groupName);
});

click('leave-group', event => {
    let groupName = getText('message-text');
    invoke(connection, 'LeaveGroup', groupName);
});

click('groupmsg', event => {
    let groupName = getText('target');
    let message = getText('private-message-text');
    invoke(connection, 'SendToGroup', groupName, message);
});

click('send', event => {
    let data = getText('me-message-text');
    invoke(connection, 'Echo', data);
});

</script>

值得注意的是,你可能会发现此文档中没有 signalr-client.js,它是如何来的呢?有两种方法

第一种方法是下载 SignalR 源代码,找到 Client-TS 项目,编译 TypeScript 即可获得。

第二种相对简单的方法是通过 Npm 在线获取

npm install signalr-client --registry https://dotnet.myget.org/f/aspnetcore-ci-dev/npm/
© . All rights reserved.