ServiceStack is an independent, self-sufficient, light-weight framework built on top of ASP.NET for building robust end-to-end web applications.
In the previous edition of the DNC .NET Magazine (Issue 15th Nov-Dec 2014), we introduced Service Stack and saw how easily one can build web apps and web APIs using this great framework. The same article can also be accessed online over here.
If you haven’t read the previous article yet, I would encourage you to read it and check the sample code before continuing further. Code sample of the previous article contains a Student Reports application that performs basic operations on a database and shows data on Razor views. In this article, we will continue adding the following features to the same sample:
- Authentication and authorization
- Bundling and minification
- Markdown views
- ..and we will briefly discuss how to create a self-hosted ServiceStack application.
Authentication and Authorization in ServiceStack
Security is an integral part of any enterprise application. Every framework that allows us to create great apps should also provide ways to secure the app for legitimate users. ServiceStack comes with an easy and effective solution for applying security on its components.
In the period of last 2 years or so, OAuth has gotten a lot of attention. We see a number of popular public sites allowing users to authenticate using their existing accounts on Google, Facebook, Twitter or a similar site. ServiceStack offers this to us for free of cost; with minimum setup required to get it up and running.
Authentication Providers and Repositories
To enable authentication and authorization in a ServiceStack app, we don’t need to install a new NuGet package to the app. The feature is included in core package of the framework. The framework includes a number of auth providers; following are most commonly used ones:
- CredentialsAuthProvider: Authenticates based on Username and Password
- AspNetWindowsAuthProvider: For windows authentication
- BasicAuthProvider: Provider for Basic authentication
- DigestAuthProvider: Provides Http digest authentication
- TwitterAuthProvider: OAuth provider to authenticate a user with Twitter account
- FacebookAuthProvider: OAuth provider to authenticate a user with Facebook account
- GoogleAuthProvider: OAuth provider to authenticate a user with Google account
We can write our own custom authentication provider by either implementing ServiceStack.Auth.IAuthProvider interface or by extending ServiceStack.Auth.AuthProvider class. It is also possible to write our own OAuthProvider by extending the class ServiceStack.Auth.OAuthProvider.
In the sample application, we will add credentials based authentication and Twitter authentication. Let’s start with credentials authentication.
Credentials Authentication
To enable credentials authentication and to provide users to register, we need to add the following statements to Configure method in AppHost.cs:
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[]{
new CredentialsAuthProvider()
}));
Plugins.Add(new RegistrationFeature());
The CredentialsAuthProvider needs a repository to store/retrieve user information. The framework has a set of built-in repositories that work with some of the most widely used data storage mechanisms. Following are the repositories available:
- OrmLiteAuthRepository: To work with an RDBMS to manage user data
- RedisAuthRepository: To use Redis as data source containing user information
- InMemoryAuthRepository: Stores user information in main memory
- MongoDBAuthRepository: To use MongoDB NoSQL DB as data source containing user information
- RavenUserAuthRepository: To use RavenDB NoSQL DB as data source containing user information
These repositories serve most of the common cases, and you can write your own repository by implementing ServiceStack.Auth.IUserAuthRepository. In the sample application, since we are already using SQL Server for application data; let us use the same database for storing user data. So OrmLiteAuthRepository is our obvious choice.
The OrmLiteAuthRepository class is defined in ServiceStack.Server assembly. Let’s add it using NuGet.
Install-package ServiceStack.Server
The OrmLiteAuthRepository needs an IDbConnectionFactory object to perform its action. All we need to do to use OrmLiteAuthRepository is create an object with an IDbConnectionFactory and register it with the IoC container of ServiceStack, Funq. Following are the statements to be added to Configure method to perform this task:
var repository = new OrmLiteAuthRepository(ormLiteConnectionFactory);
container.Register(repository);
The OrmLiteAuthRepository instance needs some of tables to work with. As we don’t have the tables already created in our DB, we can ask the repository to create them for us. For this, the repository has a method called DropAndReCreateTables. As the name suggests, it would drop and re-create all the tables. So we need to add a condition to check if one of the tables is already created. This might not be the best possible check, but works for the demo.
using (var db = ormLiteConnectionFactory.Open())
{
if(!db.TableExists("UserAuth")){
repository.DropAndReCreateTables();
}
}
Exploring Authentication Endpoints
Now if you run the application and see metadata page of ServiceStack, you should be able to see some new endpoints automatically added for us.
Figure 1: New Endpoints
Let’s register a user using the endpoint. Open Fiddler or, Chrome’s REST Client plugin or any HTTP debugger of your choice. I am use Fiddler to play with the APIs I build. We will send a POST request to the Register endpoint listed in Figure-2.
To know how to use this endpoint, click the JSON link (You can click any of the corresponding links; we will use the JSON endpoint in our app). It shows the options and format of the data to be posted to the endpoint.
Figure 2: Data format and options
In your HTTP debugging tool, compose a POST request following the structure in figure 3. Figure 4 shows how to do it in Fiddler.
Figure 3: POST Request
After executing this request, you would get the following response indicating successful registration from the server:
Figure 4: Response in Fiddler
If you check the UserAuth table now, it has a row with the data you just entered:
Figure 5: Newly added user in UserAuth table
If you set autoLogin to true in the JSON data sent with the request, you would also receive a session ID along with the request. I intentionally didn’t do it so that I can show how to login using the auth API.
To login using the API, we need to send a POST request to /auth/credentials endpoint (as we are using credentials provider). You can check the request specification in the documentation. Following is the request sent to login API from Fiddler:
Figure 6: POST Request for login
If your credentials are correct, you will get a success response from server containing session ID.
Figure 7: Successful response with SessionID
Similarly for logout, we need to send a request to the following URL to get the user logged out of the app: http://localhost:/auth/logout?sessionId=
Applying Authentication on a page and Creating Login Form
Let’s impose authentication rules on the Marks API. It can be done by simply adding Authenticate attribute to the MarksRequestDto class.
[Authenticate]
public class MarksRequestDto
{
//Properties of the class
}
Now if you try to load the marks page of any student, you would be redirected to the login page. As we don’t have a view for login yet, the browser will display an error. Let’s add a simple login page to the application. We need a DTO class and a service to respond to the request.
public class LoginService : Service
{
public object Get(LoginDto request)
{
var session = this.GetSession();
if (session.IsAuthenticated)
{
var redirectionUrl = (request.Redirect == string.Empty || request.Redirect == null) ? "students" : request.Redirect;
return this.Redirect(redirectionUrl);
}
return request;
}
}
[Route("/login", Verbs="GET")]
public class LoginDto
{
public string Redirect { get; set; }
}
Add a view and name it LoginDto.cshtml. Add the following mark-up to the page:
@using StudentReports.DTOs
@inherits ViewPage
@{
ViewBag.Title = "Login";
}
@section Scripts{
}
This page needs some JavaScript to handle login. Add a JavaScript file to the application inside a new folder App and name it loginPage.js. Add the following script to this file:
(function (window) {
function activateLoginPage() {
$("#loginForm").submit(function (e) {
var formData = $(this).serialize();
var loginUrl = $(this).attr("action");
$.ajax({
url: loginUrl,
type: "POST",
data: formData
}).then(function (data) {
location.replace(decodeURIComponent(location.href.substr(location.href.indexOf("=") + 1)));
}, function (e) {
console.log(e);
alert(e.statusText);
});
e.preventDefault();
});
}
window.activateLoginPage = activateLoginPage;
}(window));
Build and run your application after saving these changes. Change the URL to http://localhost:/login. You will see the login page appearing on the screen. Login using the credentials you used to register; it should take you to home page after logging in. And now you should be able to view marks of students as well.
Note: I am not creating the register form as part of this article. Creating a register form is similar to the Login form except the fields and API to be used. I am leaving this task as an assignment to the reader.
Adding Twitter Login
Let’s add an option for the users to login using their twitter accounts. For this, you need to register your app on twitter app portal and get your consumer key and consumer secret. Once you get the keys from twitter, add following entries to your app settings section:
/students"/>
/auth/twitter"/>
"/>
"/>
Names of keys shouldn’t be modified. Finally, you need to modify the Authentication feature section in AppHost.cs as:
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[]{
new CredentialsAuthProvider(),
new TwitterAuthProvider(new AppSettings())
}));
Now, we are all set to use twitter authentication. Save all files, build and run the application. Open a browser and type the following URL:
http://localhost:/auth/twitter
This will take you to twitter’s login page and will ask you to login. Once you are logged in, it will take you to the following page:
If you click authorize app in the above page, it would take you to the home page and you will be able to access the pages that require authentication.
Similarly, you can add any other OAuth provider to authenticate a user. You can learn more about OAuth providers in ServiceStack on their wiki page.
Login/Logout link and Displaying Username
To logout, you need to send a request to the following path:
http://localhost:/auth/logout API.
This API removes user details from the session. To make it easier, you can add a link on your page.
Adding a login/logout link with name of the logged in user needs a bit of work. This is because we get user information from session in the service. We can read the user’s name from session and pass it to view through response DTO. Following snippet shows how to read username and assign it to the response DTO:
public object Get(MarksRequestDto dto)
{
var username = this.GetSession().UserName == null ? "" : this.GetSession().UserName.ToTitleCase();
//Logic inside the method
return new MarksGetResponseDto()
{
Id = student.StudentId,
Name = student.Name,
Class = student.CurrentClass,
Marks = new List() { marks },
Username = username
};
}
In the Layout page, you can use this property to show username and login/logout link. Following is the snippet:
@if (Model.Username != "" && Model.Username != null)
{
Hi @Model.Username!
Logout
}
else
{
Login
}
If you login using local account it would display your local username, and if you use an OAuth provider, it would display username of your social account.
Authorization
Authentication makes sure that the user is known to the site and authorization checks if the user belongs to the right role to access a resource. To restrict users accessing a resource based on the roles, we need to apply the RequiredRole attribute on the request DTO class. Following snippet demonstrates this:
[Authenticate]
[RequiredRole("Admin")]
public class MarksRequestDto
{
//properties in the DTO class
}
Apply this attribute to the MarksRequestDto class. Now if you try accessing the marks page for any student, the app won’t allow you (and it redirects to home page because of the changes we did to login page). You can assign role to a user using /assignroles API, but this API is restricted to users with Admin role. So let’s create a new user with admin role when the application starts. Add the following snippet inside AppHost.cs after creating the tables:
if (repository.GetUserAuthByUserName("Admin") == null)
{
repository.CreateUserAuth(new UserAuth()
{
UserName = "Admin",
FirstName = "Admin",
LastName = "User",
DisplayName = "Admin",
Roles = new List() { RoleNames.Admin }
}, "adminpass");
}
Login using these credentials and now you should be able to access the marks page.
A user can be assigned with a set of permissions too and the permissions can be checked before allowing the user to access an API. To set permissions to the admin in above snippet, assign a string list to the Permissions property:
repository.CreateUserAuth(new UserAuth()
{
UserName = "Admin",
FirstName = "Admin",
LastName = "User",
DisplayName = "Admin",
Roles = new List() { RoleNames.Admin },
Permissions = new List() { "AllEdit" }
}, "adminpass");
Following is the attribute to be applied on the API to check for the permission:
[RequiredPermission("AllEdit")]
Now you can assign a role to an existing user. To do that, login using the admin credentials and invoke the /assignroles POST API with following data:
{“username”:”ravi”, “roles”:”Admin”}
After this step, you will be able to access the marks page when you login using ravi as username.
Bundling and Minification in ServiceStack
In rich client based applications where you have a lot of client side script to be loaded during page load, loading time of the page gets affected because of number of round trips made to server to load all files. Using techniques like bundling and minification, these files can be combined and shortened to reduce the total size and number of downloads from server to make the page load faster.
ServiceStack supports bundling and minification. To enable bundling in the ServiceStack app, we need to install the Bundler Nuget package.
· Install-package Bundler
This package adds a new file Bundler.cs and a folder bundler to the project. If you expand bundler folder, it contains a node_modules folder and some executable cmd and exe files. This may pop a question in your mind that why would we need Node.js modules in a .NET application? The bundler.js file in this folder requires Node.js code to use these modules and perform the operations. This package is capable of other things including compiling LESS to CSS and CoffeeScript to JavaScript.
We have two files in App folder that we need to combine and minify. We need to create a .bundle file listing the files to be bundled. Add a new file to App folder and rename it to app.js.bundle. Add following content to this file (list of js filed to be bundled):
loginPage.js
marksRequestPage.js
By default, the package looks inside Content and Scripts folders for bundle files, but we can change it in the bundler.cmd file. Open the bundler.cmd file and modify the node command as follows:
if "%*" == "" (
node bundler.js ../App
)
Now if you run the cmd file, you will get two files:
· app.js: Contains combined content of the files mentioned in the bundle file
· app.min.js: Contains combined and minified content of the files mentioned in the bundle file
From now on, whenever you make a change to one of these files, you need to run the bundle.cmd file. This step seems tiring; so let’s automate it with the build process. Open properties of the project and specify following command under Build Events -> Post build event commandline:
"$(ProjectDir)bundler\bundler.cmd"
From now on, whenever you build the project, bundler.cmd would also run and it would produce two files in the App folder. One of these files would contain concatenated code from both files and the other would contain concatenated and minified code. To refer to the resultant file of the bundle, specify the following statement in Layout.cshtml after reference of jQuery:
@Html.RenderJsBundle("~/App/app.js.bundle", ServiceStack.Html.BundleOptions.MinifiedAndCombined)
The RenderJsBundle extension method is added in Bundler.cs file. You can play around with other options available in the enum BundleOptions by changing in the code and learn its behavior.
When you run the code after this change, you will see the above line gets replaced with a script tag similar to the following:
Markdown Razor View Engine
Markdown is tag less HTML. Meaning, you can compose HTML using plain text by following a set of conventions without writing even a single HTML tag. It is widely used these days to compose README files, post rich text on forums like Stack Overflow and even to write professional blog posts. If you have never tried markdown, check stackedit.io. The welcome page of the site contains a nice example of markdown covering most of the syntax. You can use HTML to apply any formatting that is not available in markdown.
ServiceStack’s razor view engine supports Markdown views. You can use model binding syntax that you use in regular Razor pages and compose HTML with no tags. Add a new page to Views folder and name it AboutDto.md. Add the following code to it:
Student Marks Reports of XYZ School
## About Student Reports
This app is to view and manage reports of students of XYZ School.
*Contact school management for any questions on report of your kid.*
This page was last updated on @Model.ModifiedDate by @Model.Name
Notice that we are using HTML just for applying bootstrap styles. Content of the page is free from tags. In the last statement, we are using a couple of properties from the model object. We can create a layout page for markdown and use it as base template for all markdown files. It is important to remember that Razor layout pages cannot be combined with markdown views and vice versa.
We need DTOs and service for the view. Following snippet contains the code of these classes:
[Route("/about")]
public class AboutDto
{
}
public class AboutResponse
{
public string Name { get; set; }
public DateTime ModifiedDate { get; set; }
}
public class AboutService : Service
{
public object Get(AboutDto dto)
{
return new AboutResponse() { Name = "Ravi", ModifiedDate = DateTime.Today };
}
}
Build and run the application and change the URL to http://localhost:/about. You will see the following screen in your browser:
Self-Hosting ServiceStack Apps
As stated in the first article, ServiceStack can be hosted on IIS, on a standalone application or even on mono. We don’t need to make any significant change in the way we write code to build a self-hosted ServiceStack application. The difference is only in the way we define the AppHost class.
Create a new Console application and install the ServiceStack NuGet package to it. Add a new class and change the name to EchoService. Add the following code to this file:
public class EchoService: Service
{
public object Get(EchoDto echoObj)
{
return "Hello, " + echoObj.Name;
}
}
[Route("/echo/{Name}")]
public class EchoDto
{
public string Name { get; set; }
}
It is a simple service that takes a name and echoes a hello message. To host this service, we need an AppHost class. Add a new class to the application and name it AppHost. Add following code to it:
public class AppHost : AppSelfHostBase
{
public AppHost():base("ServiceStack hosted on console app", typeof(EchoService).Assembly)
{
}
public override void Configure(Funq.Container container)
{
}
}
The difference between AppHost in web application and Self-hosted application is the base class of AppHost class. Here, we have AppSelfHostBase instead of AppHostBase as the base class. I will leave the Configure method empty here as we are not going to add a lot of logic to this application.
Now the only thing left to do is starting the server on a specified port. To do this, add the following code to the Main method in the Program class:
static void Main(string[] args)
{
var portno = 8888;
new AppHost().Init().Start(string.Format("http://localhost:{0}/",portno));
Console.WriteLine("ServiceStack started on port no: {0}. Press any key to stop the server.", portno);
Console.ReadLine();
}
Run the application. You will see a console screen with a message on it. Open a browser and type the following URL:
http://localhost:8888/
It will display the EchoService. Now change the URL to: http://localhost:8888/echo/DotNetCurry
You will see a hello message on the browser.
Conclusion
As we saw, ServiceStack is an awesome stack of technologies that makes it easier to build and secure an end-to-end application and host it on any platform depending on infrastructure provided by the enterprise. The technology has a number of other features including Logging, Caching, Filters, Validation and support for message queues. Check the official wiki pages for more details on these topics.
Download the entire source code from our GitHub Repository at bit.ly/dncm16-servicestack
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Rabi Kiran (a.k.a. Ravi Kiran) is a developer working on Microsoft Technologies at Hyderabad. These days, he is spending his time on JavaScript frameworks like AngularJS, latest updates to JavaScript in ES6 and ES7, Web Components, Node.js and also on several Microsoft technologies including ASP.NET 5, SignalR and C#. He is an active
blogger, an author at
SitePoint and at
DotNetCurry. He is rewarded with Microsoft MVP (Visual Studio and Dev Tools) and DZone MVB awards for his contribution to the community