Showing posts with label REST. Show all posts
Showing posts with label REST. Show all posts

Friday, 14 November 2014

ng-simple-rest-app

Here’s an example of a simple rest application, written to demonstrate simple techniques to get people started with Angular:

Source:  https://github.com/stevenh77/ng-simple-rest-app

Client features
- default page not being shown by the router
- automatic conversion of Json strings into JavaScript dates (generic/aop approach)
- date control bound to model object from a data service
- select control databound to model from a data service
- confirmation dialog
- rest service calls for get, post, put, delete
- bootstrap layout for form
- form validation
- app navigation from controller using location and window services
- logging abstraction
- promises for service calls including error handling
- generic application level error handling
- busy indicator
- unit tests with mocks and promises
- material design for form entry

Server features:
- simple rest endpoints
- compression
- request logging to console
- CORS enabled
- object retrieved from JS array based on key
- object removed from JS array based on key

Saturday, 8 November 2014

Creating a simple REST API with Node Express

Install dependencies

Create: server.js

Create film-data.json

Start the server

image

image

image

image

You can download and run the full source without the need to install any dependencies:  https://github.com/stevenh77/simple-rest-express-server

Friday, 7 November 2014

API Mocking

UI devs love to be able to work independently of server teams, which is where API mocking can really help out.

Quickly knock up a few end points with test data and responses returned – takes no time at all.

Great for building little working prototypes with REST service requests.

Mocky.io

image

Mockable.io

image

Sunday, 24 February 2013

KendoUI: Example using remote Rest calls

This quick example is best viewed in Chrome as I’m not testing in other browsers.

The Underlyings combo is populated from a NetFlix web rest service call which do films hence the content Smile

Here's the direct link:  http://stevenhollidge.com/blog-source-code/kendoui/index.html

Sunday, 27 May 2012

Silverlight Async Rest Wcf

Here is a simple example application that shows off:

  • “Aync” and “await” within Silverlight 5 in Visual Studio 11
  • Calling a simple RESTful WCF interface hosted with no config or svc file

image

image

image

MainPage.xaml.cs (code behind)

private async void LoadDataButton_Click(object sender, RoutedEventArgs e)
{
var request = new WebClient();
var uri = new Uri("http://localhost:5349/services/cars");
var jsonString = await request.DownloadStringTaskAsync(uri);

using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(jsonString.ToCharArray())))
{
var serializer = new DataContractJsonSerializer(typeof (Car[]));
var cars = (Car[]) serializer.ReadObject(stream);
CarsListBox.ItemsSource = cars;
}
}

Service Interface

[ServiceContract]
interface IService<T>
{
[OperationContract]
IList<T> GetAll();

[OperationContract]
T Get(string id);

[OperationContract]
void Insert(T entity);

[OperationContract]
void Update(string id, T entity);

[OperationContract]
void Delete(string id);
}

Service Implementation

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class CarService : IService<Car>
{
private readonly IRepository<Car> _repo;

public CarService()
{
_repo = new FakeCarRepository();
}

public CarService(IRepository<Car> repo)
{
_repo = repo;
}

[WebGet(UriTemplate = "Cars", ResponseFormat = WebMessageFormat.Json)]
public IList<Car> GetAll()
{
var cars = _repo.GetAll();
return cars;
}

[WebGet(UriTemplate = "Car/{id}", ResponseFormat = WebMessageFormat.Json)]
public Car Get(string id)
{
var car = _repo.GetById(id);
return car;
}

[WebInvoke(UriTemplate = "Car", Method = "POST")]
public void Insert(Car car)
{
_repo.Insert(car);
}

[WebInvoke(UriTemplate = "Car/{id}", Method = "PUT")]
public void Update(string id, Car car)
{
_repo.Update(car);
}

[WebInvoke(UriTemplate = "Car/{id}", Method = "DELETE")]
public void Delete(string id)
{
_repo.Delete(id);
}
}

DTO

[DataContract]
public class Car
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Make { get; set; }
[DataMember]
public string Model { get; set; }
[DataMember]
public int Year { get; set; }
}

How come no config or svc file?


Global.asax

public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("services", new WebServiceHostFactory(), typeof(CarService)));
}
}

Web.config

<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
</configuration>

Source


http://stevenhollidge.com/blog-source-code/SilverlightAsyncRestWcf.zip

Sunday, 20 May 2012

Silverlight and SSRS

As part of the agile mantra, release early and often, I’m publishing this blog post as I develop the solution.

Architecture

image

Wireframe Prototype

SSRS Reports in Silverlight

The reports are loaded from a RESTful JSON web service on the Silverlight host web server.  This in turn calls the web services from the SSRS to retrieve the full list of reports for the user along with the require parameter metadata.

When the user clicks on the report the parameters listed on the right hand side are automatically displayed, with the correct data type input control (text, combo, checkbox, date picker), default values and validation.

To be continued…

As more gets added to the project I’ll update this blog post.

Friday, 13 April 2012

ServiceStack: REST with ProtoBuf

Service Stack

Service Stack makes services easy! 

You can download their excellent software and examples from the website:  http://www.servicestack.net/

This blog post combines three of their examples into one project: 

  • Web Service
  • REST Services
  • ProtoBuf plugin

Web Services in 5 easy Steps

1. Create an empty ASP.NET Web Application

new-web

2. Add ServiceStack via Nuget

servicestack-via-nuget

3. Create your DTO, Response and Service

// DTO
public class Hello
{
public string Name { get; set; }
}

// Response
public class HelloResponse
{
public string Result { get; set; }
}

//Service
public class HelloService : IService<Hello>
{
public object Execute(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}

4. Add your Global.asax


Notice typeof(AppHost).Assembly on line 11.  This allows us to pass in multiple assemblies that contain our services.  In this case we’re just working within the one assembly.

using System;
using System.Web;
using Funq;
using ServiceStack.Demo.WebService;
using ServiceStack.WebHost.Endpoints;

namespace ServiceStack.Demo
{
public class AppHost : AppHostBase
{
public AppHost() : base("ServiceStack makes services easy!", typeof(AppHost).Assembly) { }

public override void Configure(Container container)
{
Routes
.Add<Hello>("/hello")
.Add<Hello>("/hello/{Name}");
}
}

public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
new AppHost().Init();
}
}
}

5. Update your Web.Config


Notice line 6: <location path=”servicestack”>  This creates a virtual directory that will contain your services, in this example we’ve called it servicestack but it could be called anything we like.

<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<location path="servicestack">
<system.web>
<httpHandlers>
<add path="*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*"/>
</httpHandlers>
</system.web>

<!-- Required for IIS 7.0 -->
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add path="*" name="ServiceStack.Factory" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" preCondition="integratedMode" resourceType="Unspecified" allowPathInfo="true" />
</handlers>
</system.webServer>
</location>

</configuration>


That’s it, ready to run!


image


Don’t panic, remember we set a virtual directory to hold all our services (see step 5)?

All we need to do is navigate to the servicestack folder.

Out of the box you get a nice metadata landing page, which lists all your hosted services:


image


If we run the default web service URL we see a nice HTML rendered presentation of our data.


image


Or we can append the format of our choice to the URL:


image


For Google Chrome to be able to render JSON I use the following plugin:


https://chrome.google.com/webstore/detail/chklaanhfefbnpoihckbnefhakgolnmc 


image


?format=JSV would return us the following data:


image


JSV is a compact version of JSON, which by default Google Chrome cannot display in the browser.


NOTE TO CLIENTS: When using other clients to your services you can set the HTTP Accept header on the request to determine which response format you’ll receive.


image


I’m using RestClient-Tool to test my services: http://code.google.com/a/eclipselabs.org/p/restclient-tool/


Rest Services


Two simples steps:


1. Add your Services

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using ServiceStack.Common.Web;
using ServiceStack.ServiceInterface;
using ServiceStack.Text;

namespace ServiceStack.Demo.Rest
{
[Description("GET or DELETE a single movie by Id. Use POST to create a new Movie and PUT to update it")]
public class Movie
{
public Movie()
{
this.Genres = new List<string>();
}

public int Id { get; set; }
public string Title { get; set; }
public decimal Rating { get; set; }
public string Director { get; set; }
public DateTime ReleaseDate { get; set; }
public List<string> Genres { get; set; }
}

public class MovieResponse
{
public Movie Movie { get; set; }
}

public class MovieService : RestServiceBase<Movie>
{
/// GET /movies/{Id}
public override object OnGet(Movie movie)
{
// normally you would return a movie from the db
return new MovieResponse { Movie = movie };
}

/// POST /movies
///
/// returns HTTP Response =>
/// 201 Created
/// Location: http://localhost/ServiceStack.MovieRest/movies/{newMovieId}
///
/// {newMovie DTO in [xml|json|jsv|etc]}
public override object OnPost(Movie movie)
{
// insert a movie into your database... returns the new Id = 999
var newId = 999;
var newMovie = new MovieResponse { Movie = new Movie() { Id = newId } };

return new HttpResult(newMovie)
{
StatusCode = HttpStatusCode.Created,
Headers = {{ HttpHeaders.Location, this.RequestContext.AbsoluteUri.WithTrailingSlash() + newId }}
};
}

/// PUT /movies/{id}
public override object OnPut(Movie movie)
{
// save to db
return null;
}

/// DELETE /movies/{Id}
public override object OnDelete(Movie request)
{
// delete from db
return null;
}
}

[Description("Find movies by genre, or all movies if no genre is provided")]
public class Movies
{
public string Genre { get; set; }
}

public class MoviesResponse
{
public List<Movie> Movies { get; set; }
}

public class MoviesService : RestServiceBase<Movies>
{
/// GET /movies
/// GET /movies/genres/{Genre}
public override object OnGet(Movies request)
{
return new MoviesResponse { Movies = new List<Movie>() { new Movie() { Id=10 }} };
}
}
}

2. Add your routes to the Global.asax

public override void Configure(Container container)
{
Routes
.Add<Hello>("/hello")
.Add<Hello>("/hello/{Name}");

Routes
.Add<Movie>("/movies", "POST,PUT,DELETE")
.Add<Movie>("/movies/{Id}")
.Add<Movies>("/movies")
.Add<Movies>("/movies/genres/{Genre}");
}

Run the app!


Our services have been detected.


image


image


Adding ProtoBuf for Raw Performance


1. Add the plugin via Nuget


protobuf


Which adds WebActivator framework which runs code before the start of application in the ASP.NET pipeline.


ProtoBuf_AppStart is added and will run to handle the configuration.


2. Add Attributes to your DTOs


ProtoBuf requires attributes to explain the de/serialization order for the properties.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Runtime.Serialization;
using ServiceStack.Common.Web;
using ServiceStack.ServiceInterface;
using ServiceStack.Text;

namespace ServiceStack.Demo.Rest
{
[DataContract]
[Description("GET or DELETE a single movie by Id. Use POST to create a new Movie and PUT to update it")]
public class Movie
{
public Movie()
{
this.Genres = new List<string>();
}

[DataMember(Order = 1)] public int Id { get; set; }
[DataMember(Order = 2)] public string Title { get; set; }
[DataMember(Order = 3)] public decimal Rating { get; set; }
[DataMember(Order = 4)] public string Director { get; set; }
[DataMember(Order = 5)] public DateTime ReleaseDate { get; set; }
[DataMember(Order = 6)] public List<string> Genres { get; set; }
}

[DataContract]
public class MovieResponse
{
[DataMember(Order = 0)]
public Movie Movie { get; set; }
}

public class MovieService : RestServiceBase<Movie>
{
/// GET /movies/{Id}
public override object OnGet(Movie movie)
{
// normally you would return a movie from the db
return new MovieResponse { Movie = movie };
}

/// POST /movies
///
/// returns HTTP Response =>
/// 201 Created
/// Location: http://localhost/ServiceStack.MovieRest/movies/{newMovieId}
///
/// {newMovie DTO in [xml|json|jsv|etc]}
public override object OnPost(Movie movie)
{
// insert a movie into your database... returns the new Id = 999
var newId = 999;
var newMovie = new MovieResponse { Movie = new Movie() { Id = newId } };

return new HttpResult(newMovie)
{
StatusCode = HttpStatusCode.Created,
Headers = {{ HttpHeaders.Location, this.RequestContext.AbsoluteUri.WithTrailingSlash() + newId }}
};
}

/// PUT /movies/{id}
public override object OnPut(Movie movie)
{
// save to db
return null;
}

/// DELETE /movies/{Id}
public override object OnDelete(Movie request)
{
// delete from db
return null;
}
}

[DataContract]
[Description("Find movies by genre, or all movies if no genre is provided")]
public class Movies
{
[DataMember(Order = 1)]
public string Genre { get; set; }
}

[DataContract]
public class MoviesResponse
{
[DataMember(Order=1)]
public List<Movie> Movies { get; set; }
}


public class MoviesService : RestServiceBase<Movies>
{
/// GET /movies
/// GET /movies/genres/{Genre}
public override object OnGet(Movies request)
{
return new MoviesResponse { Movies = new List<Movie>() { new Movie() { Id=10 }} };
}
}
}

Ready to run!


Notice how the X-PROTOBUF column has been added.


image


image


image


image


Framework tests for ServiceStack,Plugins.ProtoBuf can be found here:



Blog Source

http://stevenhollidge.com/blog-source-code/ServiceStack.Demo.zip

Friday, 30 September 2011

WcfWebApi REST JSON Services

Overview

Here is an example of using WcfWebApi to produce a RESTful JSON data service.  We have three areas to consider:

  • Our data (model) is a simple Contact class
  • Our service is exposed via IContractService interface and implemented as ContractService
  • Our data store (repository) is exposed via IContactRepository interface and implemented as ContractRepository

diagram

RESTful Interface

Data Operation HTTP Verb Has Side Effects? Idempotent? Example Uri
Read All GET No Yes http://localhost:8000/contacts
Read Single GET No Yes http://localhost:8000/contacts/1
Insert POST Yes No http://localhost:8000/contacts
Update PUT Yes Yes http://localhost:8000/contacts
Delete DELETE Yes Yes http://localhost:8000/contacts/3

HTTP Status (Return) Codes

Http Code Description Examples
200 OK Successful (read, update or delete)
201 CREATED Successful (insert)
400 BAD REQUEST Passing id as “X” when an integer is expected
404 NOT FOUND Item not found (based on id)
500 SERVER ERROR Exception thrown within service on server with the message contained within response body.

Service Interface

[ServiceContract]
public interface IContactService
{
[OperationContract]
[WebGet(UriTemplate = "")]
HttpResponseMessage GetAll();

[OperationContract]
[WebGet(UriTemplate = "{id}")]
HttpResponseMessage Get(string id);

[OperationContract]
[WebInvoke(UriTemplate = "", Method = "POST")]
HttpResponseMessage Insert(Contact contact);

[OperationContract]
[WebInvoke(UriTemplate = "", Method = "PUT")]
HttpResponseMessage Update(Contact contact);

[OperationContract]
[WebInvoke(UriTemplate = "{id}", Method = "DELETE")]
HttpResponseMessage Delete(string id);
}

Service Implementation

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using Microsoft.ApplicationServer.Http.Dispatcher;
using RestDemo.Data;
using RestDemo.Service.Helpers;
using RestDemo.Service.Repositories;

namespace RestDemo.Service
{
public class ContactService : IContactService
{
private const string ID_MUST_BE_INTEGER = "Id must be an integer";
private const string ITEM_NOT_FOUND = "Item not found";
private readonly IContactRepository _repo;

public ContactService() : this(new ContactRepository())
{
}

public ContactService(IContactRepository repository)
{
_repo = repository;
}

#region IContactService Members

public HttpResponseMessage GetAll()
{
IEnumerable<Contact> contacts;
DataOperationResponse dataResponse = _repo.GetAll(out contacts);
HttpContent httpContent = GetHttpContent(contacts);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse, httpContent);
return httpResponse;
}

public HttpResponseMessage Get(string id)
{
int intId;
if (!Int32.TryParse(id, out intId))
return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(ID_MUST_BE_INTEGER) };

Contact contact;
DataOperationResponse dataResponse = _repo.Get(intId, out contact);
HttpContent httpContent = GetHttpContent(contact);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse, httpContent);
return httpResponse;
}

public HttpResponseMessage Insert(Contact contact)
{
DataOperationResponse dataResponse = _repo.Insert(contact);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse);
return httpResponse;
}

public HttpResponseMessage Update(Contact contact)
{
DataOperationResponse dataResponse = _repo.Update(contact);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse);
return httpResponse;
}

public HttpResponseMessage Delete(string id)
{
int intId;
if (!Int32.TryParse(id, out intId))
return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(ID_MUST_BE_INTEGER) };

DataOperationResponse dataResponse = _repo.Delete(intId);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse);
return httpResponse;
}

#endregion

private static HttpContent GetHttpContent(int id)
{
HttpContent httpContent = new StringContent(Convertors.ToJson(id).ToString(), Encoding.UTF8, MediaTypes.JSON);
return httpContent;
}

private static HttpContent GetHttpContent(Contact contact)
{
HttpContent httpContent = contact
!= null
? new StringContent(Convertors.ToJson(contact).ToString(), Encoding.UTF8,
MediaTypes.JSON)
: null;
return httpContent;
}

private static HttpContent GetHttpContent(IEnumerable<Contact> contact)
{
HttpContent httpContent = contact
!= null
? new StringContent(Convertors.ToJson(contact).ToString(), Encoding.UTF8,
MediaTypes.JSON)
: null;
return httpContent;
}

private static HttpResponseMessage SetupHttpResponse(DataOperationResponse dataResponse, HttpContent content = null)
{
var response = new HttpResponseMessage();

switch (dataResponse.Status)
{
case DataOperationStatus.Success:

if (dataResponse.Type == OperationType.Insert)
{
response.StatusCode = HttpStatusCode.Created;
response.Content = GetHttpContent(dataResponse.InsertedId);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(30));
}
else
{
response.StatusCode = HttpStatusCode.OK;

if (content != null)
response.Content = content;
}

break;

case DataOperationStatus.NotFound:

// REST best practise on delete is to return OK if resource
// cannot be found but here we are explicitly returning Not Found
response.StatusCode = HttpStatusCode.NotFound;
response.Content = new StringContent(ITEM_NOT_FOUND);
break;

case DataOperationStatus.Exception:
response.StatusCode = HttpStatusCode.InternalServerError;
response.Content = new StringContent(dataResponse.Exception.Message);
break;
}

return response;
}

/* You might also want set the ocation header:
* response.Headers.Location = new Uri("Contacts/" + item.Id, UriKind.Relative);
*/
}
}

Service Unit Tests


Service-Unit-Tests


Hosting The Service in an ASP.NET Web Application

public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
var webApiConfig = new WebApiConfiguration {EnableTestClient = true};
RouteTable.Routes.SetDefaultHttpConfiguration(webApiConfig);
RouteTable.Routes.MapServiceRoute<ContactService>("Contacts");
}
}

Alternatively…. Console Hosting


The source code also contains an example of hosting within a client application:

private const string CONTACTS_URI = "http://localhost:8010/contacts";

private static void Main(string[] args)
{
using (var host = new HttpServiceHost(typeof (ContactService), CONTACTS_URI))
{
host.Open();

ExitOnReadKey();
}

private static void ExitOnReadKey()
{
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
}

Client Access to the REST service interface from a Console App

private static void HttpGetAll()
{
var client = new HttpClient();
HttpResponseMessage response = client.Get(CONTACTS_URI);
Console.WriteLine("GET: {0} RESPONSE: {1} \n{2}\n", CONTACTS_URI, response.StatusCode, response.Content.ReadAsString());
}

private static void HttpPost()
{
var client = new HttpClient();
HttpContent jsonContactNew = new StringContent("{\"Id\":0,\"Name\":\"Edward\"}", Encoding.UTF8, MediaTypes.JSON);
HttpResponseMessage response = client.Post(CONTACTS_URI, jsonContactNew);
Console.WriteLine("POST (INSERT CONTACT): {0} RESPONSE: {1} \n{2}\n", CONTACTS_URI, response.StatusCode, response.Content.ReadAsString());
}

private static void HttpPut()
{
var client = new HttpClient();
HttpContent jsonContactUpdated = new StringContent("{\"Id\":2,\"Name\":\"Ziggy\"}", Encoding.UTF8, MediaTypes.JSON);
HttpResponseMessage response = client.Put(CONTACTS_URI, jsonContactUpdated);
Console.WriteLine("PUT (UPDATE CONTACT ID 2): {0} RESPONSE: {1}\n", CONTACTS_URI, response.StatusCode);
}

private static void HttpDelete()
{
var client = new HttpClient();
const string uri = CONTACTS_URI + "/3";
HttpResponseMessage response = client.Delete(uri);
Console.WriteLine("DELETE (CONTACT ID 3): {0} RESPONSE: {1} \n", uri, response.StatusCode);
}

private static void HttpGet()
{
var client = new HttpClient();
const string uri = CONTACTS_URI + "/1";
HttpResponseMessage response = client.Get(uri);
Console.WriteLine("GET: {0} RESPONSE: {1} \n{2}\n", uri, response.StatusCode, response.Content.ReadAsString());
}

// Some methods worth knowing:
// 1. When expecting JSON in response body you can set the following header in the request:
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypes.JSON));
//
// 2. When deserializing JSON single item within response body use the following:
// var contact = response.Content.ReadAs<Contact>();
//
// 3. When deserializing JSON collection within response body use the following:
// var contacts = response.Content.ReadAs<Contact[]>();

Output


ScreenShot163


Monitoring


HTTP


You can use Fiddler to monitor all HTTP requests and responses.  The screenshot below shows Fiddler on the left hand side picking up all requests and responses from the WCF Web API Test Client (explained within Debugging below) on the right hand side.


Fiddler-Monitoring


Debugging


Configure the Firewall (if required)


Make sure you punch a hole in your firewall before testing (or change the port from 8010 to 80 in the source code). 


On Windows 7 > Control Panel > Windows Firewall > Allow a Program or Feature through the Firewall


ScreenShot152


Fiddler


We are now ready to open Fiddler and use the Request Builder tab to enter your URI, set the HTTP verb (defaults to GET) and execute your request.  On the left hand side you’ll see the HTTP response and headers, in this case the result was 200 (OK):


Fiddler-GET-example1


Then switch to the Inspectors tab to view the response body containing our data.


Fiddler-GET-example2


For PUT and POST requests you must add the following to the request headers:


Content-Type: application/json; charset=utf-8


Fiddler-POST-exampleFiddler-PUT-example


WCF Web API Test Client


We switched this new Microsoft Tool on in the Global.asax page with “EnableTestClient = true” which automatically routes:



In our case this would be http://localhost:8010/Contacts/Test:


ScreenShot153ScreenShot156ScreenShot157ScreenShot158ScreenShot159


 


Full source code:  http://stevenhollidge.com/blog-source-code/RestDemo.zip

Thursday, 16 June 2011

Agile RESTful Json in Silverlight

Following on from my previous post (Consuming POX in Silverlight), here’s a simple example of consuming a Json data service.

ScreenShot012

Data file (exposed via Http Handler on Web Server)

[
{
name:'Shredded Wheat',
price:4.95,
description:'Two per serving with skimmed milk',
calories:650
},
{
name:'Fresh Fruit Salad',
price:5.95,
description:'Strawberries, bananas, apples and pears',
calories:400
},
{
name:'Scrambled Eggs on Toast',
price:3.95,
description:'Two eggs on two slices of wholewheat toast',
calories:300
},
{
name:'Bacon Roll',
price:2.5,
description:'Three slices of lean bacon in a granary roll',
calories:600
},
{
name:'Homestyle Breakfast',
price:12.95,
description:'Two eggs, bacon, sausage, toast and orange juice',
calories:950
}
]

Http Handler (on Web Server)

using System.IO;
using System.Web;

namespace AgileREST.Web
{
public class FoodService : IHttpHandler
{

public void ProcessRequest(HttpContext context)
{
var dataPath = HttpContext.Current.Server.MapPath("Food.json");

using (var reader = new StreamReader(dataPath))
{
var result = reader.ReadToEnd();
context.Response.ContentType = "text/json";
context.Response.Write(result);
}
}

public bool IsReusable
{
get
{
return false;
}
}
}
}

Mapping (on the client)

namespace AgileREST
{
public class Food
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
public int Calories { get; set; }
}
}

Service Call (on the client)

using System;
using System.Collections.Generic;
using System.Json;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;

namespace AgileREST
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}

void webClientForJson_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error != null) return;

// first option
JsonArray foods = (JsonArray)JsonArray.Load(e.Result);
var query = from food in foods
select new Food()
{
Name = (string)food["name"],
Price = (decimal)food["price"],
Description = (string)food["description"],
Calories = (int)food["calories"]
};
listboxFoodJson.ItemsSource = query.ToList();

// second option
// for implicit serialisation requires identical case sensitive name across Json and C# food objects)
//DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(List<Food>));
//List<Food> foods = (List<Food>) jsonSerializer.ReadObject(e.Result);
//listboxFoodJson.ItemsSource = foods;
}

private void btnGetFoodJson_Click(object sender, RoutedEventArgs e)
{
WebClient webClientForXml = new WebClient();
webClientForXml.OpenReadCompleted += webClientForJson_OpenReadCompleted;
webClientForXml.OpenReadAsync(new Uri(HtmlPage.Document.DocumentUri, "FoodService.ashx"));
}
}
}

Data binding (on the client)

<UserControl x:Class="AgileREST.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="700">

<UserControl.Resources>
<DataTemplate x:Key="itemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="tbName"
Width="170"
Text="{Binding Path=Name}" />
<TextBlock x:Name="tbPrice"
Width="50"
Text="{Binding Path=Price}" />
<TextBlock x:Name="tbDescription"
Width="300"
Text="{Binding Path=Description}" />
<TextBlock x:Name="tbCalories"
Width="50"
Text="{Binding Path=Calories}" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>

<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Width="600" Margin="10">
<TextBlock Text="Json Example" Margin="5" />
<StackPanel x:Name="ColumnDescriptions" Orientation="Horizontal" Margin="5" >
<TextBlock Text="Name" Width="170" />
<TextBlock Text="Price" Width="50" />
<TextBlock Text="Description" Width="300" />
<TextBlock Text="Calories" Width="50" />
</StackPanel>
<ListBox x:Name="listboxFoodJson"
Height="150"
Margin="5"
ItemTemplate="{StaticResource itemTemplate}" />
<Button x:Name="btnGetFoodJson"
Height="50"
Margin="5"
Click="btnGetFoodJson_Click"
Content="Get Food From Json Service" />
</StackPanel>
</Grid>
</UserControl>

You can download the source code from the following link:


http://stevenhollidge.com/blog-source-code/AgileREST.zip