Showing posts with label Fiddler. Show all posts
Showing posts with label Fiddler. Show all posts

Saturday, 6 July 2013

C# client for Server Side Events (EventSource)

Examples of one way streaming from server to client can be found on the web but it’s pretty much always JavaScript clients.  Here is a working example using the .NET stack, with WebAPI as the server and a C# console application as the client.

Download and run this WebAPI chat application which emits Server Side Events:

https://github.com/filipw/html5-push-asp.net-web-api/

I then open Visual Studio running as admin, update the code to use IIS with my machine name (Zeus) and a virtual directory, clicking the create virtual directory button, so I can track requests using Fiddler:

image

Then update the JavaScript within the app to use the same path:

image

The create a Console application, using NUGET add Json.NET and paste this code in:

using System;
using System.IO;
using System.Net;
using System.Text;
using Newtonsoft.Json;

namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
new WebClientWrapper();
Console.ReadKey();
}
}

public class WebClientWrapper
{
WebClient wc { get; set; }

public WebClientWrapper()
{
InitialiseWebClient();
}

// When SSE (Server side event) occurs this fires
private void OnOpenReadCompleted(object sender, OpenReadCompletedEventArgs args)
{
using (var streamReader = new StreamReader(args.Result, Encoding.UTF8))
{
var cometPayload = streamReader.ReadLine();
var jsonPayload = cometPayload.Substring(5);
var message = JsonConvert.DeserializeObject<Message>(jsonPayload);
Console.WriteLine("Message received: {0} {1} {2}", message.dt, message.username, message.text);
InitialiseWebClient();
}
}

private void InitialiseWebClient()
{
wc = new WebClient();
wc.OpenReadAsync(new Uri("http://zeus/chatapp/api/chat/"));
wc.OpenReadCompleted += OnOpenReadCompleted;
}
}

public class Message
{
public string username { get; set; }
public string text { get; set; }
public string dt { get; set; }
}
}


You’ll need to update the code for your machine name.


Now run Fiddler, the WebAPI project and the console app and add a message in the chat window:


image


In Fiddler, select the request your console just made and select Raw, you’ll see nothing.  Now right click and select COMETPeek and you’ll see the payload that was streamed.


image

Tuesday, 12 March 2013

Fiddler Extension: JsonpViewer

Here’s an example of a Fiddler Extension I started building for CORS workarounds using JsonP.

https://github.com/stevenh77/JsonpViewer

Some background, I’m currently using Jsonp for cross domain service calls.  As only GETS are allowed, and I want to pass data to my services as Json, I add a request query parameter which is URI encoded.  In order to debug and cleanly read the object I’ve added a inspector to Fiddler to extract this object, decode it and display within fiddler UI.

I’ve also wrote this blog post to remind me how to create an extension next time.

image

Here we can see a Json object being passed via a URI encoded HTTP GET request:

GET http://localhost:2626/api/dealdates?request=%7B%22strikeAbsolute%22%3A0.0%2C%22strikePips%22%3A1000.0%2C%22markupAbsolute%22%3A0.0%2C%22markupPercentage%22%3A0.0%2C%22clientPriceAbsolute%22%3A5.0%2C%22clientPricePercentage%22%3A0.1%2C%22currencyPair%22%3Anull%2C%22spotAsk%22%3A0.0%2C%22notional%22%3A0.0%2C%22expiry%22%3A%220001-01-01T00%3A00%3A00%22%7D HTTP/1.1
User-Agent: Fiddler
Host: localhost:2626

Click over into my new “Json Query Params” window and you get the Json object nicely formatted and display:

image

Notes:

This extension currently relies upon the JSON object being passed as “?request=” as this fit my pattern and needs.

The example above uses the same json for both the request and response, as I was too lazy to create another object and it’s late now so almost time for bed…

If you’re interested in building your own extension here’s some tips:

1. Grab my source code as a reference:  https://github.com/stevenh77/JsonpViewer

2. Switch on logging within Fiddler:

Alt + Q opens the ExecAction command line (lower left hand corner, the black bar)

Enter the following commands:

prefs set fiddler.debug.extensions.verbose True

prefs set fiddler.debug.extensions.showerrors True

image

To confirm your settings have been updated enter this command:    about:config

image

3. Now when you open Fiddler you can select the log tab to see your extension loaded.

Remember:  When deploying your class library, the DLLs for Inspectors go in the {program files}\Fiddler2\Inspectors folder

image

4. If your DLL isn’t loading remember to add a Fiddler.RequiredVersion attribute to the AssemblyInfo.cs file.

image

6.  Build your project as .NET Framework 2 or 4.  I dont think Framework 4.5 is not supported yet but I havent tested that.  My project was built with .NET 2.0 and later upgraded to .NET 4.0.  Both worked great the my version of Fiddler (4.4.3.0)

image

Good luck!

Friday, 26 October 2012

Fiddler HTTPS

To setup Fiddler to show HTTPS request/responses you’ll need to export your client certificate from Internet Explorer into the Fiddler directory and activate HTTPS within Fiddler.

Here is how:

Internet Explorer > Tools > Options > Content tab > Click Certificates buttons:

2

Click the Export button:

3

45

6

 

1

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