Friday, 31 October 2014
Tuesday, 22 October 2013
ExcelMediaTypeFormatter
Working example of implementing an ExcelMediaTypeFormatter for WebApi.
Source: https://github.com/stevenh77/ExcelFormatterForWebApi/
Full blog write up to follow….
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:
Then update the JavaScript within the app to use the same path:
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:
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.
Wednesday, 3 July 2013
WebApi.SelfHost
using System.Web.Http;
using System.Web.Http.SelfHost;
namespace Workflow.Console
{
class Program
{
static void Main(string[] args)
{
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute(
"API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
System.Console.WriteLine("Press Enter to quit.");
System.Console.ReadLine();
}
}
}
}
using System.Collections.Generic;
using System;
using System.Web.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Workflow.Console
{
public class DocumentUploadController : ApiController
{
public Guid Post([FromBody] DocumentRequest reportData)
{
var id = Guid.NewGuid();
Task.Factory.StartNew(() => CreateReport(id.ToString(), reportData.Parameter1));
return id;
}
private void CreateReport(string key, string documentData)
{
Thread.Sleep(5000);
DocumentDownloadController.Cache.Add(key, "GENERATED DOCUMENT: " + documentData);
}
}
public class DocumentDownloadController : ApiController
{
public static Dictionary<string, string> Cache = new Dictionary<string, string>();
public DocumentResponse Get(string id)
{
string document;
var response = new DocumentResponse { Id = id };
if (Cache.TryGetValue(id, out document))
{
response.IsAvailable = true;
response.Document = document;
}
else
{
response.IsAvailable = false;
}
return response;
}
}
public class DocumentRequest
{
public string Parameter1 { get; set; }
}
public class DocumentResponse
{
public string Id { get; set; }
public bool IsAvailable { get; set; }
public string Document { get; set; }
}
}
With XML:
or add this line to Program Main and you can remove the namespace from the xml:
config.Formatters.XmlFormatter.UseXmlSerializer = true;
Saturday, 2 March 2013
How to return Jsonp from WebApi
This allows you to make service requests from jQuery to a different domain (cross site scripting).
1. Add a Formatters folder to your WebApi project and add the following class:
public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
{
private readonly HttpRequestMessage _request;
private string _callbackQueryParameter;
public JsonpMediaTypeFormatter()
{
SupportedMediaTypes.Add(DefaultMediaType);
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));
MediaTypeMappings.Add(new UriPathExtensionMapping("jsonp", "application/json"));
}
public JsonpMediaTypeFormatter(HttpRequestMessage request)
: this()
{
_request = request;
}
public string CallbackQueryParameter
{
get { return _callbackQueryParameter ?? "callback"; }
set { _callbackQueryParameter = value; }
}
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
if (type == null)
throw new ArgumentNullException("type");
if (request == null)
throw new ArgumentNullException("request");
return new JsonpMediaTypeFormatter(request) { SerializerSettings = SerializerSettings };
}
public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
{
string callback;
if (IsJsonpRequest(_request, out callback))
{
var writer = new StreamWriter(stream);
writer.Write(callback + "(");
writer.Flush();
return base.WriteToStreamAsync(type, value, stream, content, transportContext).ContinueWith(_ =>
{
//TODO: Inspecting the task status and acting on that is better
writer.Write(")");
writer.Flush();
});
}
return base.WriteToStreamAsync(type, value, stream, content, transportContext);
}
private bool IsJsonpRequest(HttpRequestMessage request, out string callback)
{
callback = null;
if (request == null || request.Method != HttpMethod.Get)
{
return false;
}
var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
callback = query[CallbackQueryParameter];
return !string.IsNullOrEmpty(callback);
}
}
2. Add a FormatterConfig class to your App_Start folder
public class FormatterConfig
{
public static void RegisterFormatters(MediaTypeFormatterCollection formatters)
{
formatters.Remove(formatters.JsonFormatter);
formatters.Insert(0, new JsonpMediaTypeFormatter
{
SerializerSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
}
});
}
}
3. Update your Global.asax.cs file to register the FormatterConfig
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
FormatterConfig.RegisterFormatters(GlobalConfiguration.Configuration.Formatters);
}
}
That's it!
How to test your service call
Now your services can be called for Json or Jsonp.
Json
You can continue to get your Json response in the normal way: http://localhost:2626/api/underlyings
Jsonp
You can now call your service passing in the callback to receive Jsonp:
$.getJSON("http://localhost:2626/api/underlyings/jsonp?callback=?", function (result) {
// process response
});
You’ll see here that the response wraps the data in a jQuery function.
Here’s the HTML code I used in this example:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Jsonp test</title>
<script src="Scripts/jquery-1.9.1.min.js" type="text/javascript"></script>
</head>
<body>
<button id="getjsonp">Get JSONP</button>
<h4>Request result:</h4>
<div id="jsonp"></div>
<script type="text/javascript">
$(document).ready(function () {
$("#getjsonp").click(function () {
$.getJSON("http://localhost:2626/api/underlyings/jsonp?callback=?", function (result) {
$(result).each(function(index, item) {
$('#jsonp').append('<p>' + item.currencyPair + '</p>');
});
});
});
})
</script>
</body>
</html>
For a working downloadable example I recommend downloading the WebApiContrib solution for Jsonp:
https://github.com/WebApiContrib/WebApiContrib.Formatting.Jsonp