CodeProject
In the second part of this series, you learned about the MVC pattern. ASP.NET, Katana and you created your first managed service using Visual Studio. This part will cover the basics of custom controllers.
Custom Controllers
Custom controllers are usual Web API controllers that you would use in standard Web API and MVC projects. To add a new custom controller to our sample project, “OldSchoolBeats
”, right click your “Controllers” folder and choose “Add>Controller…”.
The “Add Scaffold” dialog will pop-up. Choose the “Windows Azure Mobile Services Custom Controller” template, and click “Add”. Name the new custom controller “OldSchoolFeedsController
”:
The new controller will be added to the “Controllers” folder. Like I said before, it is a basic Web API controller, with one difference: There is a public
member “Services
” of type ApiServices
defined. You may wonder what this thing is good for and where it’s actually been set.
The ApiServices
type can be accessed within your whole managed services project. It is instantiated using dependency injection. The DI-Container used for managed services is Autofac. And because ApiServices
is a property, it is done via property-injection. If you want to learn a bit more about Autofac and how the wiring works within the managed backend, I highly recommend this blog-post by Henrik F. Nielsen – he’s a member of the Azure Mobile Services team:
His blog-post provides great resources about Autofac as well. And make sure you stop by on a regular basis – all blog-posts are worth your time.
The ApiServices Class
Like the name states, “ApiServices
” has something to do with the core services offered by the managed backend. Here are the “services” that are directly accessible via the ApiServices
class.
- The
HttpConfiguration
instance - The Log instance
- Access to the push-client
- Access to the settings dictionary
Without the ApiServices
class, it wouldn’t be possible to access the current HttpConfigurationl
, to log messages, to send out push-notifications or to access the current settings on the server.
Setting the Access-levels Using the AuthorizationLevelAttribute
Security is very important, not only when you create a new managed service. You should invest a fair amount of time to work-out a basic security strategy. Re-think and re-shape that strategy on a regular base, using all the input-parameters you can get – log-files, firewall-data whatever you can get to make your service more secure and reliable for your users. The standard authorization-level is: Application
.
The AuthorizationLevelAttribute
gives you a fair amount of options to set the security:
- for the whole controller
- or just for a specific controller-action (method)
Four different authorization-levels are available:
- Anonymous (Anyone can access the controller or action)
- Application (Only requests that include the application-key can access the controller or action)
- User (Only requests that include a valid authentication-header can access the controller or action)
- Admin (Only requests that include the master-key as a header-value can access the controller or action)
Please remember that the AuthorizationAttribute
cannot only be used on custom-controllers but on TableControllers
and other controller-types as well.
For now, we want to use the AuthorizationLevel.User
on our OldSchoolFeedsController
. That way, we make sure that only authenticated users can access this controller and its actions (methods).
Returning RSS2.0 from our Database-entries using our Custom Controller
Let’s implement now the GET
-Method in our custom controller. Open the file “OldSchoolFeedsController.cs” and implement the GET
-Method using the following code. Please notice the new return-type of the method. It’s not “string
” anymore, it’s of type “HTTPResponseMessage
”:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.ServiceModel.Syndication;
using System.Web.Http;
using System.Web.Http.Description;
using Microsoft.WindowsAzure.Mobile.Service;
using Microsoft.WindowsAzure.Mobile.Service.Security;
using OldSchoolBeats.Models;
using OldSchoolBeats.DataObjects;
using System.Xml;
using System.Text;
namespace OldSchoolBeats.Controllers
{
[AuthorizeLevel(AuthorizationLevel.User)]
public class OldSchoolFeedsController : ApiController
{
public ApiServices Services { get; set; }
private ServiceUser currentMobileServicesUser;
public HttpResponseMessage Get()
{
if (this.User.Identity.IsAuthenticated)
{
this.currentMobileServicesUser = (ServiceUser)this.User;
}
SyndicationFeed feed =
new SyndicationFeed("Old School Feed", "The coolest old-school rappers.",
new Uri("http://www.allmusic.com/subgenre/old-school-rap-ma0000002762/albums"),
"OLDSFEED", DateTime.Now);
feed.Language = "en-US";
List<OldSchoolArtist> artists = new List<OldSchoolArtist>();
List<SyndicationItem> feedItems = new List<SyndicationItem>();
using (var db = new OldSchoolBeatsContext())
{
artists = db.OldSchoolArtists.OrderBy(a=>a.CreatedAt).Take(50).ToList();
}
foreach (var artist in artists)
{
var rssEntry = new SyndicationItem()
{
Title = new TextSyndicationContent(artist.Artist,
TextSyndicationContentKind.Plaintext),
Id = new Guid().ToString(),
PublishDate = DateTime.Now,
Summary = new TextSyndicationContent(
string.Format("{0} was active between {1}
and was added to our database {2}.", artist.Artist,
artist.YearsArchive, artist.CreatedAt),
TextSyndicationContentKind.Plaintext
),
Copyright = new TextSyndicationContent
("ALLMUSIC.COM", TextSyndicationContentKind.Plaintext)
};
feedItems.Add(rssEntry);
}
var sb = new StringBuilder();
XmlWriter w = XmlWriter.Create(sb);
feed.SaveAsRss20(w);
w.Flush();
w.Close();
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(sb.ToString(),
Encoding.Unicode, "application/rss+xml");
return response;
}
}
}
In the first few lines, you can see how to obtain the current user (if the user has been authenticated). If not, the current user is null
(currentMobileServicesUser
). On the next lines, an instance of the SyndicationFeed class is created.
This class allows you to create Atom or RSS-Feeds in a very simple way, that way you don’t have to fiddle around with xml-documents. First the feed needs to be created, then the items and the last step is to write the feed to an XML writer.
Then a new instance of the OldSchoolBeatsContext
is created. This is the class that allows us, to access the data (our tables) using Entity Framework. Using LINQ, we just take the 50 latest entries from the database. One line of code that makes us happy.
The last third of the code handles the generation of the RSS 2.0 feed. To output the content of the XMLWriter
instance, we use a StringBuilder
. Please don’t forget to flush the contents of the writer and to close it. This ensures that the underlying stream is actually written into the StringBuilder
. The rest of the code returns a new HttpResponseMessage
with a status code of 200 (which basically means that everything is ok). The desired return type is “application/rss+xml
” which is the mime-type used for a RSS feeds. Even if we have no data in our OldSchoolArtists
table, we still get a response of 200 and the basic construct for an valid RSS 2.0 feed.
If you hit F5 now to debug the managed service locally and you type in the following URL => http://localhost:9215/api/OldschoolFeeds (the port number 9215 should be different from mine) in Internet Explorer for example, you will get the RSS-Subscription view:
As long as you don’t delete the table “OldSchoolArtists
”, everything should work just fine.
In the next and last part, we will create the universal app and implement the complete set of crud operations on Windows 8.1 and Windows Phone 8.1.
You can download the source here: OldSchoolBeats on GitHub