Dealing with Expired Channels in Windows Azure Mobile Services
What’s this? Another Windows Azure Mobile Services (WAMS) post?!
In the next version of my app, I keep a record of the user’s Channel in order to send down notifications. The built in todo list example does this or something very similar. My table in WAMS looks like:
Not shown are a couple of fields, but of particular interest is the device Id. I realized that one user might have multiple devices, so the channel then is tied to the device Id. I still haven’t found a perfect way to do this yet – right now, I’m using a random GUID on first run.
In my WAMS script, if the point that is submitted is “within range” of another user, we’ll send a notification down to update the tile. I go into this part in my blog post: Best Practices on Sending Live Tiles. But what do you do if the channel is expired? This comes up a lot in testing, because the app is removed/reinstalled many times.
I stumbled on this page, Push Notification Service Request and Response Headers, on MSDN. There is a lot of great info on that page. While I should have more robust solution for handling all these conditions, the one in particular I’m interested in is the Channel Expired response, highlighted below:
HTTP response code | Description | Recommended action |
---|---|---|
200 OK | The notification was accepted by WNS. | None required. |
400 Bad Request | One or more headers were specified incorrectly or conflict with another header. | Log the details of your request. Inspect your request and compare against this documentation. |
401 Unauthorized | The cloud service did not present a valid authentication ticket. The OAuth ticket may be invalid. | Request a valid access token by authenticating your cloud service using the access token request. |
403 Forbidden | The cloud service is not authorized to send a notification to this URI even though they are authenticated. | The access token provided in the request does not match the credentials of the app that requested the channel URI. Ensure that your package name in your app's manifest matches the cloud service credentials given to your app in the Dashboard. |
404 Not Found | The channel URI is not valid or is not recognized by WNS. | Log the details of your request. Do not send further notifications to this channel; notifications to this address will fail. |
405 Method Not Allowed | Invalid method (GET, DELETE, CREATE); only POST is allowed. | Log the details of your request. Switch to using HTTP POST. |
406 Not Acceptable | The cloud service exceeded its throttle limit. | Log the details of your request. Reduce the rate at which you are sending notifications. |
410 Gone | The channel expired. | Log the details of your request. Do not send further notifications to this channel. Have your app request a new channel URI. |
413 Request Entity Too Large | The notification payload exceeds the 5000 byte size limit. | Log the details of your request. Inspect the payload to ensure it is within the size limitations. |
500 Internal Server Error | An internal failure caused notification delivery to fail. | Log the details of your request. Report this issue through the developer forums. |
503 Service Unavailable | The server is currently unavailable. | Log the details of your request. Report this issue through the developer forums. |
Obviously getting a new channel URI is ideal, but the app has to do that on the client (and will) next time the user runs the app. In the mean time, I want to delete this channel because it’s useless. In my script which sends the notifications, we’ll examine the result on the callback and either delete the channel if expired, or, if success, send a badge update because that’s needed, too. (Future todo task: try to combine Live Tile and badges in one update.)
push.wns.send(channelUri, payload, 'wns/tile',
{
client_id: 'ms-app://<my app id>',
client_secret: 'my client secret',
headers: { 'X-WNS-Tag' : 'SomeTag' }
},
function (error, result) {
if (error)
{
//if the channel has expired, delete from channel table
if (error.statusCode == 410){
removeExpiredChannel(channelUri)
}
}
else
{
//notification sent
updateBadge(channelUri);
}
}
);
Removing expired channels can be done with something like:
function removeExpiredChannel(channelUri)
{
var sql = "delete from myapp.Channel where ChannelUri = ?";
var params = [channelUri];
mssql.query(sql, params,
{
success: function(results) {
console.log('Removed Expired Channel: ' + channelUri)
}
});
}
On my todo list is to add more robust support for different response codes – for example, in addition to a 410 response, a 404 would also want to delete the channel record in the table.