Generating a link to an ApiController isn't too difficult once you know the right methods to call.
In a Razor view, for example, you can generate a link to an API controller using the standard Url helper property. The following link will point to an Albums controller and pass along an id parameter with a value of 3:
@Url.RouteUrl("DefaultApi", new { httproute=true, controller="Albums", id=3})
Notice the route name is "DefaultApi", which is the default route name for WebAPI routes, but you can chose any API route name you need. Also notice the presence of the httproute value, which disambiguates WebAPI routes from other MVC routes. You can avoid the httproute parameter if you use the HttpRouteUrl helper method instead:
@Url.HttpRouteUrl("DefaultApi", new { controller="Albumns", id=3 })
An action parameter isn't needed, since API routing uses the HTTP method by default (but you could specify an action in the route parameters, if need be).
An ApiController has a Url helper property with two interesting methods: Link and Route. Link always returns an absolute URL, while Route can produce a relative URL.
The following code uses Link and will produce a URL pointing to the same controller where the code executes:
Url.Link("DefaultApi", new { id = 3 })
If you want a link to a different controller, just pass the controller name along in the route values (along with any other route values the route might support):
Url.Link("DefaultApi", new { controller = "Albums", id = 3})
Note: both the Link and Route methods add an httproute key to the route parameters you pass in. Technically then, you shouldn't use the Url property in an ApiController to generate non-WebAPI links, although it generates the right URL (because regular MVC route handlers don't care about the presence of httproute).
This concludes the extreme excitement of link generation.
This post is a commentary on the statement: "We are sticking with WCF because we want a service oriented architecture."
SOA is a three letter acronym that created a fervor from the server closet to the executive boardroom. A decade of labor by standards committees, tool vendors, framework builders, consultants, and hucksters made sure SOA became the silver bullet for enterprise architecture.
If I look through the noise for the pragmatic essence of SOA, I see three noble goals:
- Software encapsulation – or the ability to hide complexity behind coarse grained interfaces.
- Software autonomy - or the absolute control of the execution environment and the resources required by software, as well as the freedom to make independent decisions about implementation details.
- Software interoperability – or optimizing for reuse by relying on standard application level communication protocols.
Those three goals are entirely possible with HTTP based services and the ASP.NET WebAPI. The S in SOA doesn't stand for SOAP.
I know many people will say the additional metadata commonly associated with SOA (like WS-Metadata Exchange, WS-Policy, and XML Schema, to name just a few) is also a core part of SOA. I'd argue the metadata was good for vendors who built software to manage the complexity of the metadata.
KISS it goodbye with the WebAPI.
Let's say you have the following type definition in C#:
public class Thing { public int Id { get; set; } public string FirstName { get; set; } public string ISBN { get; set; } public DateTime ReleaseDate { get; set; } }
The default JSON serialization of a thing will look like this:
{"Id":1,"FirstName":"Scott","ISBN":"123","ReleaseDate":"2013-03-24T16:26:33.7719981Z"}
It feels awkward to work with JavaScript objects where the first letter of a property name is a capital letter. But, it feels awkward to work with C# objects where the first letter of a property name is not capitalized.
Fortunately, the default serializer (Json.NET) will let you have the best of both worlds. All you need is a bit of configuration during application startup:
var formatters = GlobalConfiguration.Configuration.Formatters; var jsonFormatter = formatters.JsonFormatter; var settings = jsonFormatter.SerializerSettings; settings.Formatting = Formatting.Indented; settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
The CamelCasePropertyNamesContractResolver will produce the following JSON instead:
{ "id": 1, "firstName": "Scott", "isbn": "123", "releaseDate": "2013-03-24T16:39:28.4516517Z" }
We also tweaked the formatting settings to make the JSON easier for humans to read.
Henceforth we begin a series of tips and musings on the WebAPI.
Tip #1 – HttpStatusCode is an enum you can use to send specific status codes in a response. Going through the list, it is not always clear what name matches a value you are looking for, so here is a WebAPI controller to dump status codes into JSON.
public class StatusCodesController : ApiController { public HttpResponseMessage Get() { var type = typeof(HttpStatusCode); var values = Enum.GetNames(type) .Select(name => new { name =name, value = (int)Enum.Parse(type, name) }); var response = new HttpResponseMessage(HttpStatusCode.OK); response.Content = new ObjectContent(values.GetType(), values, Configuration.Formatters.JsonFormatter); return response; } }
Tip #2 – The Request.CreateResponse extension method will use a content negotiation strategy to pick the best formatter for the response. In cases where you want to force a specific content type, use the approach in the above code. This example is forcing a JSON request by passing in the JsonFormatter to the ObjectContent ctor (because the XML formatter doesn't like anonymous types).
More to come...
Given this markup:
<div id="special" data-timeOut="3"> </div>
And this script code:
$(function() { var special = $("#special"); var specialData = special.data(); for (var p in specialData) { if (specialData.hasOwnProperty(p)) { special.text(p); } } });
Answer the following questions:
What output will appear when executing the code on Chrome or IE10?
a) timeOut
b) timeout
c) (nothing)
d) ice cream
What output will appear when executing the code on IE9?
a) timeOut
b) timeout
c) (nothing)
d) yellow pencil
Let's change data-timeOut to data-time-out:
<div id="special" data-time-out="3"> </div>
What output will appear when executing the code on IE 10, IE9, or Chrome?
a) timeOut
b) timeout
c) (nothing)
d) whale teeth
1: B
2: C
3: A
Always use the data-some-name style, which gives you camel cased names in JavaScript (someName).
Never use camel case in the data- attribute itself.
RTFM, particularly the section titled "The algorithm for getting the list of name-value pairs".
Glimpse is highly extensible. If there is something you want to know about on the server, it's relatively easy to write a plugin that will feed data to the Glimpse dashboard. There is some documentation in the Glimpse GitHub wiki on writing an extension. This post provides a slightly more complex example to show how a Glimpse dashboard tab can interact with data collectors using a message broker.
Let's say you are adding additional IDisplayMode objects into the global ASP.NET MVC DisplayModeProvider. These display modes will select device specific views (an iPhone view, a Kindle Fire view, etc). Now, you want to look in the Glimpse dashboard to easily see what providers are registered, the order of evaluation, and which of the providers the runtime selected for a particular request.
There are various extension points in ASP.NET MVC that will let you tap into the processing pipeline. Quite a bit of the core Glimpse.Mvc3 package relies on proxy objects that wrap runtime components (like the view engines) and feed data to Glimpse when interesting things happen (how a view is selected, for example). Once you find a way to tap into the runtime, feeding data to Glimpse is the easy part.
One way to tap into the runtime is to use a global filter (perhaps not the best approach, but it is simple for this demonstration). Here is a result filter that will find the current display mode being used and send the mode to Glimpse as part of a message.
public class GlimpseDisplayModeDump : IResultFilter { private readonly IMessageBroker _broker; public GlimpseDisplayModeDump(IMessageBroker broker) { _broker = broker; } public void OnResultExecuting(ResultExecutingContext filterContext) { } public void OnResultExecuted(ResultExecutedContext filterContext) { var mode = filterContext.DisplayMode; if (_broker != null) { _broker.Publish(new DisplayModeMessage(mode)); } } }
The key to the Glimpse <-> IResultFilter interaction is the Glimpse message broker. The message broker allows us to publish messages of any type, and an interested party can subscribe to these messages and aggregate or summarize information from the messages. When the filter executes, it will look at the display mode for the request (passed by MVC as part of the filter context), and pass the mode as part of a message via the message broker.
The message itself is a simple object that encapsulates the display mode and provides a simple property to retrieve the name (ModeId) of the display mode.
public class DisplayModeMessage { public DisplayModeMessage(IDisplayMode mode) { Mode = mode; } public IDisplayMode Mode { get; set; } public string ModeId { get { return Mode.GetName(); } } }
In the above code, GetName is an extension method to compute the name of a display mode. In MVC, the IDisplayMode interface doesn't provide a lot of information, so we'll check to see if the object is actually a DefaultDisplayMode object.
public static class DisplayModeExtensions { public static string GetName(this IDisplayMode mode) { var asDefault = mode as DefaultDisplayMode; if (asDefault != null) { if (!String.IsNullOrEmpty(asDefault.DisplayModeId)) { return asDefault.DisplayModeId; } return "Default DefaultDisplayMode"; } if(mode != null) { return mode.GetType().Name; } return "None"; } }
An object implementing the Glimpse ITab interface has a 1:1 correlation with tabs that appear in the client-side dashboard. It is the job of the tab to provide a name that appears in the dashboard, and to provide data to display in the tab when Glimpse is ready. Here's a tab to show the display mode data.
public class DisplayModeTab : TabBase, ITabSetup { public void Setup(ITabSetupContext context) { GlobalFilters.Filters.Add(new GlimpseDisplayModeDump(context.MessageBroker)); context.PersistMessages<DisplayModeMessage>(); } public override object GetData(ITabContext context) { var result = new List<object[]>(); var message = context.GetMessages<DisplayModeMessage>().FirstOrDefault(); var id = message != null ? message.ModeId : "<none>"; result.Add(new object[] { "Key", "Value" }); result.Add(new object[] { "Selected Display Mode", id }); result.Add(new object[] { "All Display Modes", DisplayModeProvider.Instance .Modes .Select(mode => mode.GetName()).ToArray() }); return result; } public override string Name { get { return "DisplayMode"; } } }
The GetData method is the key to producing data for a request. In this sample the code will find the DisplayModeMessage for the request, as well as look at all of the registered display modes, and produce a loose collection of what is essentially key/value pairs of data for display.
All of the above code can go directly into your MVC project, or into a separate assembly with a reference to Glimpe.Core (the preferred approach). Once the assembly is in the web app's bin directory, Glimpse will pick it up and start interacting with the tab to show data in the browser dashboard. More advanced formatting is possible, too.