Monday, July 16, 2012

Create a Web API in .Net–In Asp.Net 4.0 Web Forms VB.Net

What is an API?  In simple terms, it is a protocol.  A protocol is a defined method of communication that contains a predetermined format so that both sides of the conversation know how to interact with each other.  This being said, an API is a predetermined format for utilizing data.  The two predominate forms in today’s format are JSON and SOAP.  There are many a heated conversation in the geek world of which is better to use.  There are also many blogs, articles, and other periodicals that attempt to sway the developer to one technology over the other.  Microsoft has attempted to solve the issue for the developer.  In Web API (Available since Asp.Net MVC 4.0 – as a Release Candidate) it defaults to JSON.

Here’s what we are going to attempt to do in this article.  We are going to make a VB.Net based ASP.Net 4.0 Web Forms site (with the help of AJAX) that utilizes Web API.  If you have not installed ASP.Net MVC 4.0 you will need to install a couple packages to be able to utilize Web API.
Prerequisites:
You can install ALL the Web API packages using Nuget (Obviously, you will need the Nuget extension installed in the Extension Manager).  To install Nuget packages, use the command line package manager.  In VS2010, Goto Tools | Library Package Manager | Package Manager Console -
image

From here you will get the following window:
image

From there you need to type in the following command -
image

So it will look like -
image

Through the magic of Powershell, it will install the packages.
On my particular install, it took a little more than 10 minutes to download/install/and remove backup files completely.  Good news is, now you are ready to implement Web API.
Keeping with my tradition of using VB.Net for my examples, we’ll develop in VB for this article.  First thing we need to create is a Web Application (as opposed to a Website).  So you will need to go to FILE | New Project | Visual Basic | Web | ASP.NET Web Application -
image

Name your site accordingly, and give it a location which you are comfortable with.  You are now presented with the default content for a ASP.NET Web Application for the 4.0 .Net Framework.
(Obviously, you will need the 4.0 Framework installed both on your development machine and Server which you plan to deploy to in order for the site to function appropriately.  Also, it is EXTREMELY important to ensure that the .Net Framework is set to 4 when creating the application.  Located in the top left of the window pane).
With anytime we are working with data, it is important to start with the datasource.  In this example, we are going to use the Northwind database for example data and connect to the Products table.  So in our web.config we need to create the connection to the datasource using the following format -

<add name="connName" connectionString="Server=ServerName;DataBase=DBName;user 
id=Username;password=Password" providerName="System.Data.SqlClient"/>





Obviously, for the ServerName, DBName, Username, and Password properties, you will want to add your specific and appropriate values.


Now down to the nitty gritty.  We’ll need to add a controller.  Right Click in the Solution Explorer on your Solution Name | Add | New Item.  In the modal window, make sure you are under the Visual Basic | Web templates.  Select Web API Controller Class -


image



 



Here’s where you need to think ahead a little bit.  In the Name field you have the default name ValuesController1.vb.  Web API uses prefix naming conventions to identify the controller in the web request.  Thus, if you name the controller ProductsController.vb your API will identified as Products.  If you name it OrderController.vb, it will be identified as Order.  Make sure to take note if you name it as singular or plural.  Relieve some headaches for yourself.  In this article, I am going to name it ProductsController.vb.  Microsoft goes ahead and gives you some default code.  This is nice and all, but we’ll end up deleting most of it and replacing it.

Next we need to add our routing to attach to the controller handler.  So, here we need to open the Global.asax file and add the route to the Application_Start.


First we need to add the Routing Namespace to the scope of the Global.asax declarations -




Imports System.Web.Routing
Imports System.Web.Http




Now it is all ready for us to add our route -



Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)         RouteTable.Routes.MapHttpRoute(name:="DefaultApi", _
routeTemplate:="api/{controller}/{id}", _
defaults:=New With {.id = System.Web.Http.RouteParameter.Optional})     End Sub


This is all we need to do to add the route into the routing table for the application.  So effectively, if we ran the application and went to the route, it’ll use the Controller like so -

*** IMPORTANT NOTE ***


Internet Explorer can not natively consume JSON, so it tries to save the file as an unknown file type as opposed Chrome and Firefox will provide the data in HTML format -


Result in IE:


image



Result in Chrome:

image



By following the routing table using the following URLs




























Method



URL


GetProducts() api/products/
GetProductByID() api/products/1
GetProductByName() api/procuts/?name=Chai



So now we need to write the methods to accomplish this. First thing we need to create is our Product Object.  So we need to Right Click on our Solution Name in the Solution Explorer | Add | Class.  In this instance, I will name my class Product.vb. 
We want to expose the following properties for the Product object – Name, Quantity, Price, and Units.  This is enough to get the point across on how to get records and display them through the API.  So now we need the code to expose these properties -




Public Class Product

Public Property Name As String
Public Property
Quantity As String
Public Property
Price As Double
Public Property
Units As Integer

End Class




Notice the keyword Public, this is what allows us to expose the property outside of the object and make it available to other methods.  Now we need to create how we will populate the object -



Public Sub New()
End Sub
Public Sub New
(ByVal name As String, ByVal quantity As String,_
ByVal price As Double, ByVal units As Integer)
Me.Name = name
Me.Quantity = quantity
Me.Price = price
Me.Units = units
End Sub




Ok, what we are doing here is creating what is called Constructors.  We are building the object.  We are also “morphing” the Sub New().  One that creates a New() method that accepts no parameters and one that accepts 4 (four) parameters name, quantity, price and units.  You will see where we use this in our helper ConvertReader method, which we need to create next -



Public Function ConvertReader(ByVal reader As SqlDataReader)
Dim products As List(Of Product) = New List(Of Product)
If reader.HasRows Then
While
reader.Read()
products.Add(New Product(reader.Item("ProductName").ToString(), _
reader.Item("QuantityPerUnit").ToString(), _
CDbl(reader.Item("UnitPrice")), _
CInt(reader.Item("UnitsInStock"))))
End While
End If
Return
products
End Function




We are creating a List of Product which holds the array of Product values.  Then we check that the reader has rows and “read” the SqlDataReader.  For each row (or record) in the reader, we are creating a NEW product to add to the list.

*** Important Note ***


You need the System.Data.SqlClient namespace in order to utilize the SqlDataReader.  So you can either -




Imports System.Data.SqlClient

Or in your ConvertReader method refer to the reader variable as -


ByVal reader As System.Data.SqlClient.SqlDataReader


Called strong type namespacing.



Now that we have the helper method to get the SqlDataReader converted to a Product Object, we need to pull the values from the database.  We need to import another namespace to help facilitate reading from the web.config -



Imports System.Web.Configuration.WebConfigurationManager







Now for our GetProducts() method -



Public Function GetProducts() As IEnumerable(Of Product)
Dim cn As SqlConnection = _
New SqlConnection(ConnectionStrings("dataconnect").ToString())
Dim cmd As New SqlCommand("dbo.GetProducts", cn)
cmd.CommandType = CommandType.StoredProcedure
cn.Open()
Dim reader As SqlDataReader = cmd.ExecuteReader()
Dim products As List(Of Product) = ConvertReader(reader)
cn.Close()
Return products
End Function




You will notice that I refer to a SP called dbo.GetProducts.  Very simple SP -



Use [Northwind]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE
dbo.GetProducts
-- Add the parameters for the stored procedure here
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

-- Insert statements for procedure here
SELECT * From dbo.Products
END
GO




Now we need to go back to our controller and delete the GetValues() Method that Microsoft defaulted there.  We need to delete GetValues() and replace it with GetProducts()-



Public Function GetProducts() As IEnumerable(Of Product)
Dim Product As Product = New Product
Return Product.GetProducts()
End Function




Now if we run that and then go to the correct url (…instance/api/products/) we get-

image



 



This is the simplest function of the API, get all records.  Here we are returning 77 records.  Now much like another other API, for instance Google, you can expose your data to other applications that outside (or inside) developers create.  They simply have to consume your API.  But what if you didn’t want to show all your Products, you just wanted to show one?  By ID?

Well now we need to create a new property ID.  So let’s add the ID Property to Products -




Public Property ID As Integer
Public Property
Name As String
Public Property
Quantity As String
Public Property
Price As Double
Public Property
Units As Integer




Add it to our morphed() New method -



Public Sub New(ByVal id As Integer, ByVal name As String, _
ByVal quantity As String, ByVal price As Double, ByVal units As Integer)
Me.ID = id
Me.Name = name
Me.Quantity = quantity
Me.Price = price
Me.Units = units
End Sub

We need to change our converter method to add the property to the list like so -




Public Function ConvertReader(ByVal reader As SqlDataReader)
Dim products As List(Of Product) = New List(Of Product)
If reader.HasRows Then
While
reader.Read()
products.Add(New Product(CInt(reader.Item("ProductID")), _
reader.Item("ProductName").ToString(), _
reader.Item("QuantityPerUnit").ToString(), _
CDbl(reader.Item("UnitPrice")), _
CInt(reader.Item("UnitsInStock"))))
End While
End If
Return
products
End Function




We need to add a method to the Product object that is not IEnumerable -



Public Function GetProductByID(id As Integer) As Product
Dim cn As SqlConnection = New _
SqlConnection(ConnectionStrings("dataconnect").ToString())
Dim cmd As New SqlCommand("dbo.GetProducts", cn)
cmd.CommandType = CommandType.StoredProcedure
cn.Open()
Dim reader As SqlDataReader = cmd.ExecuteReader()
Dim products As List(Of Product) = ConvertReader(reader)
cn.Close()
Return products.Find(Function(p) p.ID = id)
End Function




Finally, on our controller, we need to create a method that accepts the ID parameter -



Public Function GetProductByID(id As Integer) As Product
Dim Product As Product = New Product
Return Product.GetProductByID(id)
End Function




From there if we run the application with the API url and an ID -

image



Now let’s write one that gets the product by Name.  We need to go to the Products object and provide a method there (once again) -



Public Function GetProductByName(name As String) As IEnumerable(Of Product)
Dim cn As SqlConnection = New _
SqlConnection(ConnectionStrings("dataconnect").ToString())
Dim cmd As New SqlCommand("dbo.GetProducts", cn)
cmd.CommandType = CommandType.StoredProcedure
cn.Open()
Dim reader As SqlDataReader = cmd.ExecuteReader()
Dim products As List(Of Product) = ConvertReader(reader)
cn.Close()
Return products.Where(Function(p) _
String.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase))
End Function




And in our controller we need -



Public Function GetProductsByName(name As String) As IEnumerable(Of Product)
Dim Product As Product = New Product
Return Product.GetProductByName(name)
End Function




Using the correct URL we end up with -

image



So using this, we could just as easily use Units or Price.  Simply replace Name with Price on the ByName methods in the controller and on the Product object (anywhere the word Name appears simply change it to Units or Price).




With that you should be able to build your own API to suit your needs.  Next article we’ll investigate how to add CRUD operations to the API.



*** Author’s Note ***


Before I get slammed with hate mail about not using Best Practices or Tiers, I am attempting to explain a complex process with programmatic samples that the reader can then use to adapt into their project.  This article’s scope was to explain the Web API concept and not best practices.  Knowing this, it does need mention that you should always use Tiers and separate your BLL and DAL from your GUI. 



Happy .Netting…

24 comments:

  1. Chad,
    I think it is great that you put this article up. I have been strugging to find any vb.net web api resources that do not involve entity framework. I have a suggestion if you would consider modifying this tutorial. Readers would like to know where you suggest placing the overloaded Subs named New(). The same goes for the convertreader() function, both getproducts() functions, getproductbyid(), getproductbyname(), getproductsbyname(). Your article is great but would be much more helpful if you showed in what files these functions reside in a project.

    ReplyDelete
  2. Very good.

    Source Code, pelase.

    ReplyDelete
  3. Great post.thanks to share this information.In kolkata Keshri software solution is a web design and web development company.we have a very dedicated and hard working team of web application developers(asp.net/c#/sql server/MVC) , Search engine optimizers. Fast communication and quality delivery product is our commitment.

    For more details please log on to - http://www.ksoftware.co.in .

    ReplyDelete
  4. Wonderful, what a blog it is! This webpage provides useful data to us, keep it up. Love this post for so many reasons. It is so real and vulnerable, it is pure beauty.

    Kids WEbsite Design in affordable price by Optimized360.Com

    ReplyDelete
  5. Encountersnepal.com is professional and Lengal Trekking Company in Nepal
    and Base Located Trekking Agency,
    and Most top Package is Trekking in Nepal
    With fair deal.

    ReplyDelete
  6. I'd hoped you'd have details about the RouteTable.Routes.MapHttpRoute in the global.asax, but you didn't even provide how to setup a route using YOUR examples. If you add that, it'd be great!

    ReplyDelete