Sunday, August 24, 2014

ASP.NET Web Form Model Binding + Web API

Nowadays, many people are talking about Web API and some developers came to me and ask for sample code about how to call an ASP.NET Web API by using the HttpClient class library. Today, I want to share how to send HTTP GET, POST, PUT and DELETE to the ASP.NET Web API from a web page that perform simple CRUD operation.

I have created a very simple address book web application that can display and manipulate the data from the Web API. To begin with the ASP.NET Web API development, create a new empty web project from Visual Studio.



Select the Empty template, as I want to create the latest Web API (version 2.2) by getting the latest assembly from NuGet later.


From the newly created project, open NuGet Package Manager, then search for Microsoft ASP.NET Web API 2.2, then install it.


As I am using the Map HTTP Attribute Routes method to define my routes, therefore, create the class WebApiConfig as follow:

public class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API routes
        config.MapHttpAttributeRoutes();
    }

}

In the same project, add new Global.asax file if not exist. Then, at the Application_Start event, add this line of code:

GlobalConfiguration.Configure(WebApiConfig.Register);

Service

Now, proceed to create a new controller that support Get, List, Create, Update and Delete function by using HttpGet, HttpPost, HttpPut and HttpDelete attributes like following code:

[RoutePrefix("api/AddressBook")]
public class AddressBookController : ApiController
{
    // GET api/<controller>
    [Route]
    [HttpGet]
    public List<AddressBook> Get()
    {
        AddressBookComponent bc = new AddressBookComponent();
        return bc.List();
    }

    // GET api/<controller>/5
    [Route("{id:int}")]
    [HttpGet]
    public AddressBook Get(int id)
    {
        AddressBookComponent bc = new AddressBookComponent();
        return bc.Get(id);
    }

    // POST api/<controller>
    [Route]
    [HttpPost]
    public void Create(AddressBook addressBook)
    {
        AddressBookComponent bc = new AddressBookComponent();
        bc.Create(addressBook);
    }

    // PUT api/<controller>/5
    [Route("{id:int}")]
    [HttpPut]
    public void Put(int id, AddressBook addressBook)
    {
        AddressBookComponent bc = new AddressBookComponent();
        bc.Update(addressBook);
    }

    // DELETE api/<controller>/5
    [Route("{id:int}")]
    [HttpDelete]
    public void Delete(int id)
    {
        AddressBookComponent bc = new AddressBookComponent();
        bc.Delete(id);
    }

}

[RoutePrefix("api/AddressBook")] attribute is used to create a template URL for your API. As the example above, the Web API URL is base at http://localhost:<port>/api/AddressBook

[Routeattribute is used to make an URL that base on the URL defined in RoutePrefix attribute which link to the particular method directly. For this case, I never specify any named parameter to the Route attribute, hence the URL to call this web function is the same, which is http://localhost:<port>/api/AddressBook. The incoming request will route to the correct function base on the supplied HTTP Verb.

[Route("{id:int}")] has specified named parameter. This attribute will route the request URL that contain parameter with integer type to that particular function. In this case, the URL that will hit that function is http://localhost:<port>/api/AddressBook/5

[Route("{id:int}")]
[HttpPut]
public void Put(int id, AddressBook addressBook)

Note that the above HTTP PUT method contain 2 parameters, one is the ID and another is the object. This is the standard PUT method declaration. If you have the method without the ID parameter, you will get HTTP 405 Method Not Allowed error because it will treat it as POST even you had specified HttpPut attribute.

Client

Now, start to create the client that will consume the above Web API. The Web API can only support JSON and XML HTTP content type, and I pick JSON for light weight payload and it is the commonly used format nowadays. We just need to make sure the HTTP request that sent to the Web API contain the content-type : application/json in the HTTP header. And to do that, I use HttpClient class which comes from the Microsoft ASP.NET Web API 2.2 Client Libraries. You can obtain it from NuGet:



In today's topic, I will also cover about how to use model binding in grid view (ASP.NET Web Form not MVC).

NOTE: Before you begin, I expect that you already had an entity or object which defined as a model.

Create a new ASP.NET web form project, then create a new page. Put a grid view in the page, then define the grid view like this:

<asp:GridView ID="addressBookGrid" runat="server"
    ItemType="AddressBookSample.Entities.AddressBook"
    SelectMethod="addressBookGrid_GetData"
    UpdateMethod="addressBookGrid_UpdateItem"
    DeleteMethod="addressBookGrid_DeleteItem"
    DataKeyNames="ID">
    <Columns>
        <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
    </Columns>
</asp:GridView>

First, put the model as the ItemType. Then, define the SelectMethod, UpdateMethod and DeleteMethod, when you do it, Visual Studio will automatically suggest, create and map the events for you. Put the model property name in the DataKeyNames which you think it is unique and can be an identifier for your object.

HTTP GET

The following is the implementation for the SelectMethod. It will send HTTP GET request to the Web API, then bind the responded data into the grid view automatically.

public IQueryable<AddressBook> addressBookGrid_GetData()
{
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("http://localhost:5593");
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    HttpResponseMessage response = client.GetAsync("api/AddressBook").Result;
    response.EnsureSuccessStatusCode();

    List<AddressBook> addressBooks = response.Content.ReadAsAsync<List<AddressBook>>().Result;

    return addressBooks.AsQueryable();

}

HTTP POST

The following is the implementation for a Create button. I have a form with text boxes and the Create button to let user enter the information that will be sent to the Web API.

protected void btnCreate_Click(object sender, EventArgs e)
{
    AddressBook addressBook = new AddressBook()
    {
        ID = Convert.ToInt32(createID.Text),
        FirstName = createFirstName.Text,
        LastName = createLastName.Text,
        Contact = createContact.Text
    };

    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("http://localhost:5593");
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    HttpResponseMessage response = client.PostAsync<AddressBook>("api/AddressBook", addressBook, new JsonMediaTypeFormatter()).Result;
    response.EnsureSuccessStatusCode();
}

HTTP PUT

The following is the implementation for the UpdateMethod. The method will automatically have the id parameter value which you had selected from the grid view, the code will send HTTP GET request to the Web API to retrieve the specific object first, then update the object, finally send the object with HTTP PUT request to the Web API.

public void addressBookGrid_UpdateItem(int id)
{
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("http://localhost:5593");
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    HttpResponseMessage response = client.GetAsync("api/AddressBook/" + id).Result;
    response.EnsureSuccessStatusCode();

    AddressBook item = response.Content.ReadAsAsync<AddressBook>().Result;

    // Load the item here, e.g. item = MyDataLayer.Find(id);
    if (item == null)
    {
        // The item wasn't found
        ModelState.AddModelError("", String.Format("Item with id {0} was not found", id));
        return;
    }
    TryUpdateModel(item);
    if (ModelState.IsValid)
    {
        // Save changes here, e.g. MyDataLayer.SaveChanges();
        HttpResponseMessage updateResponse = client.PutAsync<AddressBook>("api/AddressBook/" + id, item, new JsonMediaTypeFormatter()).Result;
        response.EnsureSuccessStatusCode();
    }

}

HTTP DELETE

The following is the DeleteMethod implementation. The method will also automatically have the ID parameter value which you had selected from the grid view, just simply send the ID with HTTP DELETE request to the Web API as follow:

public void addressBookGrid_DeleteItem(int id)
{
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("http://localhost:5593");
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    HttpResponseMessage response = client.DeleteAsync("api/AddressBook/" + id).Result;
    response.EnsureSuccessStatusCode();

}


That's it for a simple CRUD web application that deal with Web API. If you are interested with my source code, feel free to download it from HERE.




Saturday, August 16, 2014

WCF - Duplex Named Pipe Binding

Today topic cover about how to create a WCF service which uses named pipe communication channel but also support duplex communication. In a normal web service behavior, client sends request message to the server, the server process the request then respond the message back to the client, therefore, it always need to have a requester to call the web service. What if you need your service to automatically send response to the client, but the client does not require to send request as always? Duplex communication allow server to interact with the client.

For today blog post, I have created a simple Hello World WCF service that support duplex communication and uses NetNamedPipeBinding and then the service is hosted in IIS with WAS (Windows Process Activation Service). This Hello World service allows clients to subscribe itself to the service to allow the service spam them with "Hello World!" words.

First, let's create the service first, defining the service contracts and operation contracts. In order to create a duplex communication service, we need to create a duplex contract first. This duplex contract will be used by the client to implement the service callback method, which also mean the service will be firing this callback method to send response to the client. 

Server Side

For my case, my service is going to spam "Hello World!" string to the client, so I define my duplex contract as follow:

public interface IHelloWorldServiceCallback
{
    [OperationContract(IsOneWay=true)]
    void Spam(string message);
}

Note that I have set IsOneWay as true for the named parameter in my operation contract. It is because after the client subscribed itself to the server, server automatically send message to the client, server does not expect a reply from the client.

Then, define the normal service contract as follow:

[ServiceContract(
    SessionMode=SessionMode.Required,
    CallbackContract=typeof(IHelloWorldServiceCallback))]
public interface IHelloWorldService
{
    [OperationContract]
    void SpamMe();

    [OperationContract]
    void StopSpam();
}

Note that I have specified the Session Mode named parameter with SessionMode.Required. It is required when creating a duplex communication service because the server need to keep track the client session, and then during firing the callback method, the service know the response message should be sent to which correct client.

The following is the implementation of my Hello World service contract:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class HelloWorldService : IHelloWorldService
{
    private static Dictionary<string, CancellationTokenSource> _sessionCTSs;
    static HelloWorldService()
    {
        //Create a single dictionary to keep all cancellation token source for tasks
        _sessionCTSs = new Dictionary<string, CancellationTokenSource>();
    }

    public void SpamMe()
    {
        //Client register a callback channel session for itself to the server
        var callback = OperationContext.Current.GetCallbackChannel<IHelloWorldServiceCallback>();
           
        //Get the client session Id
        var sessionId = OperationContext.Current.SessionId;

        //Server spawn a new thread that process and invoke the callback method
        var spamTaskCTS = new CancellationTokenSource();
        Task.Run(() =>
        {
            for (int i = 0; i < 100; i++ )
            {
                if (spamTaskCTS.IsCancellationRequested)
                {
                    callback.Spam("Client request stop spam!");
                    break;
                }

                callback.Spam("Hello World!");
                Thread.Sleep(1000);
            }
        }, spamTaskCTS.Token);

        _sessionCTSs.Add(sessionId, spamTaskCTS);
    }

    public void StopSpam()
    {
        //Cancel the existing thread base on Session Id
        var sessionId = OperationContext.Current.SessionId;
        var spamTaskCTS = _sessionCTSs[sessionId];
        spamTaskCTS.Cancel();
    }
}

The SpamMe() operation contract is to allow client register itself to the server that it want to subscribe the spam, asking the server to send message to it automatically. When the client invokes the SpamMe() operation, the client session ID will be kept in a dictionary. Then, a new thread will be created, the new thread will do the processing and invoke the callback method. The code above is actually sending "Hello World!" message to the client every one second for 100 times.

The StopSpam() operation contract is to allow client to request server to stop sending message to it. The method will get the client session ID from the same dictionary, and then cancel the sending message task. Once the task is cancelled, the service will not invoke the callback method anymore.

Host

Now, host the above service in IIS. The following is how my configuration file look like:

<system.serviceModel>
  <serviceHostingEnvironment multipleSiteBindingsEnabled="true">
    <serviceActivations>
      <add relativeAddress="./HelloWorldService.svc"
            service="NamedPipeDuplexSample.Services.HelloWorldService"
            factory="System.ServiceModel.Activation.ServiceHostFactory" />
    </serviceActivations>
  </serviceHostingEnvironment>
  <services>
    <service name="NamedPipeDuplexSample.Services.HelloWorldService"
              behaviorConfiguration="">
      <endpoint address="net.pipe://localhost/NamedPipeDuplexServer/HelloWorldService.svc"
                binding="netNamedPipeBinding"
                contract="NamedPipeDuplexSample.Services.Contracts.IHelloWorldService">
      </endpoint>
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior>
        <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
        <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
        <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
        <serviceDebug includeExceptionDetailInFaults="true"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

The following are the steps to configure IIS to enable named pipe protocol. Before that, you need to ensure you have install WAS (Windows Process Activation Service) in your server first.

At the website level, right click and open the Edit Bindings menu, make sure you have net.pipe binding enabled as you see in the screenshot below.


Setup and host your service at the website that you had configured just now.

At your application level, right click then go to Manage Application, open Advanced Settings menu. Enter net.pipe to enable the named pipe protocol in your application as you see in the screenshot below:



Done.

Client Side

For the client side, I have created a simple console application to test my duplex WCF service. We need to implement the callback interface which we had defined in the server side code above. 

public class HelloWorldServiceCallback : IHelloWorldServiceCallback
{
    public void Spam(string message)
    {
        Console.WriteLine(message);
    }
}

So, when the server invoke the callback method, this method at client side will be fired with the message from the server.

The following is the code of how to connect to the WCF service hosted in IIS with named pipe channel:

class Program
{
    static void Main(string[] args)
    {
        var address = new EndpointAddress("net.pipe://localhost/NamedPipeDuplexServer/HelloWorldService.svc");
        var binding = new NetNamedPipeBinding();
        var callback = new HelloWorldServiceCallback();
        var context = new InstanceContext(callback);
        var factory = new DuplexChannelFactory<IHelloWorldService>(context, binding, address);
        var service = factory.CreateChannel();

        service.SpamMe();
        Console.ReadKey();

        service.StopSpam();
        Console.ReadKey();
    }
}

Once done, simply run the console program, then you will get the result as follow:


You even can run multiple console program to test the WCF session. Simply hit one key in one of the console program, only the correct one console will stop, the rest are remain untouched.



If you are interested with my source code, feel free to get it from HERE.




Send Transactional SMS with API

This post cover how to send transactional SMS using the Alibaba Cloud Short Message Service API. Transactional SMS usually come with One Tim...