Build Your Own Conferencing System With ASP - NET MVC - Part 1
Build Your Own Conferencing System With ASP - NET MVC - Part 1
This is part 1 of a new 2-part series, and when we are finished, we will have the following functionality
in our application:
1. Register users and only allowing a custom domain as registration email (in this case sinch.com)
Sinch supports both regular phone calling and WebRTC calling for conferencing, as well as one-on-
one conversations. When I built this system I started out from the .NET Conference Calling template
and went from there. The complete solution can be downloaded from GitHub here.
Now that this is out of the way, what we want to do is to enable both regular phones and web
browsers to join a conference. The ICE callback information can help us in customizing the
functionality for each use case.
1. Calling in from a phone - We want to play a prompt to the user to enter the conference PIN in
order to be connected
2. When calling from a browser - We just want to present a PIN code window even before they
attempt to call, and then connect the browser caller with no further interaction
Controllers/CallbackController.cs
if (conference != null) {
// connect the caller to the conrefence with the correct CLI
builder.ConnectConference(conference.ConferenceId.ToString()).WithCli(cli);
builder.Say(", Welcome to the conference");
} else {
builder.Say("Invalid code").Hangup(HangupCause.Normal);
}
}
}
That’s really all there is to it for the backend. I am sure you you are curious about the conference
model, so let’s look at the conference creation.
I decided that for creating a conference, I wanted a protect login using ASP.NET identity, which is
nothing fancy and you can read about how to use it here.
Creating A Conference
So to have a more “real” conference system I wanted set an expiry time for the conference and some
basic info like who is the owner of the conference, etc. All of these fields will make more sense when
we look at the creation of the conference page.
ConferenceModels.cs
ConferenceController.cs This is a pretty big controller as it hosts both the functionality to join a
conference and to create a conference. Let’s take a look at Create. There are two actions where you
create a conference.
[Authorize]
[Route("~/Conference/Create")]
[HttpGet]
public async Task<ActionResult> Create() {
var model = new CreateConferenceModel();
//Set up some standard values to make it look nice in the UI
model.Conference = new Conference();
model.Conference.ConferenceEndDate = DateTime.Today.AddDays(5);
model.Conference.OwnerId = User.Identity.Name;
}
model.Conference.PinCode = code;
return View(model);
}
Authorization
Notice that in our implementation I don’t allow anonymous creation of conferences and so the
[Authorize] statement is added. Next up is saving the conference with its attendees. If you want to
allow that just remove [Authorize].
[Authorize]
[Route("~/Conference/Create")]
[HttpPost]
public async Task<ActionResult> Create(CreateConferenceModel model) {
using (var db = new ConferenceContext()) {
model.Conference.ConferenceId = Guid.NewGuid();
var utcdate = model.Conference.ConferenceEndDate.ToUniversalTime();
model.Conference.ConferenceEndDate = utcdate.Date;
model.Conference.OwnerId = User.Identity.Name;
db.Conferences.Add(model.Conference);
await db.SaveChangesAsync();
}
return RedirectToAction("MyConferences");
}
So now that we've a conference, I took the decision to not allow the user to set the PIN themselves
because I felt it was a better user experience not to fail on duplicate codes.
The other Action of significance in ConferenceContoller.cs is Details, if you are the owner of the
conference, you can see details and the current callers in the conference.
[Authorize]
[Route("~/Conference/{id}")]
public async Task<ViewResult> Details(Guid id) {
var model = new ConferenceDetailsViewModel();
using (var db = new ConferenceContext())
{
var conference =
db.Conferences
.FirstOrDefault(m => m.OwnerId == User.Identity.Name
&& m.ConferenceId == id);
model.Conference = conference;
try
{
var conf = await Getconference(conference.ConferenceId.ToString()).Get();
// store the participants in the result model
if (conf != null)
{
model.Participants = conf.Participants;
}
else
{
model.Participants = new IParticipant[0];
}
}
catch (Exception)
{}
return View(model);
}
}
[Route("~/Conference/Callout")]
public async Task<JsonResult> CallOut(string number, string conferenceId) {
try
{
var factory =
new WebApiClientFactory().CreateClient<ICalloutApiEndpoints>(
"https://api.sinch.com",
new ApplicationSigningFilter(appKey, Convert.FromBase64String(appSecret)),
new RestReplyFilter());
number = number.StartsWith("+") ? number.Trim() : "+" + number.Trim();
await factory.AddParticipant(new CalloutRequest
{
method = "conferenceCallout",
conferenceCallout = new ConferenceCallout
{
cli = "+17864088194",
destination = new Destination {endpoint = number, type = "number"},
domain = "pstn",
conferenceId = conferenceId,
enableDice = true
}
});
return Json(null, JsonRequestBehavior.AllowGet);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
return Json(null, JsonRequestBehavior.AllowGet);
}
As you might notice, I’ve created a brand new API endpoint on the SinchServerSDK . The backend
team rolls out features so quickly so sometimes it’s hard to keep up with the SDK parts of it.
Fortunately, it’s super easy to use the Sinch.WebAPI client to handle request signing etc. You can
read more about callouts in our documentation. The above method is posted by pressing the green
button with a JavaScript snippet in views/conference/details.cshtml
Finished
Now we actually have a real life conference calling system with some security using PIN codes.