8. Too many
concerns
1. Routing
2. Authentication/Authorization
3. Other HttpConfiguration settings
4. Action selection
5. Action result
6. Use of MessageHandlers
7. HttpResponseMessage details
8. Content formatting
20. ApiRouter
One of many contributions from Darrel Miller
Routing via HttpMessageHandler
(a.k.a. function)
Goal: full control over resource routing
Goal: nested resources
21. Functions all the
way!
Could you build an API using only Message
Handlers?
Could you build an API with functions only?
22. Frank
Wrap in an HttpMessageHandler
F# for first-class function support
Barely scratches the surface
Desire for first-class HTTP infrastructureREST Starter KitStarted in WCF as WCF Web APIBecame more alike to MVCAdopted into ASP.NET
MVC-style framework for building HTTP-based web services
Builds on top of the new, first-class HTTP support in System.Net.Http, available in .NET 4.5 or via NuGet.
Abstracts hosting model so you can run on IIS or self-hostHttpServer can also run as an in-memory host
BecauseHttpServer can run in-memory, testing is really easy. You can use HttpClient, another part of System.Net.Http, or the even lighter-weight HttpMessageInvoker.DEMO: Create a test for the default Web API project
This is great. I’m sure you’ll agree that this is far simpler than what is generally required for testing, such as WatiN or Selenium.However, we are also testing far more than just the server action.That’s a lot of things to verify.On a recent project using Web API, this bit me hard. I tested a number of these pieces, but having so many parts in play at one time distracted me from identifying all the things I wanted to test.In particular, routing, security, and IoC distracted me enough that I neglected to test some basic properties of my HttpResponseMessages, such as correctly returning 201 Created when a new item was created. I eventually remembered to set those tests and identified the problem, but I would much prefer to have identified those first and resolved any issues in the other items later.Action selection may seem a bit odd, as that should be part of the framework. However, if you try to get creative with your routing or add more than one Get/Post/etc. method to your controller, you’ll find this isn’t always trivial.I don’t want to imply that testing all of these things is unimportant. You may want to test all of these; you may only care about the higher or lower levels (integration vs. unit). That’s up to you.
What I want to highlight is an approach I’ve found useful.I want to gain a level of understanding of where my problems are occurring, so I focus my tests on multiple levels.Most of the time, I find my problems are in routing or other HttpConfiguration settings.That’s difficult to diagnose, however, when you are only testing at the highest level.We need to focus our tests to verify the behavior we want in the request -> response interaction.
I like to focus my lowest level tests on the Request -> Response interaction.This is the base for HTTP, and all of this can happen easily within one test.In the end, this is what my API should be exposing.This is also a very simple function, and functions are easy to test.
It should come as no surprise that the most basic ApiController action is one that takes a request and returns a response. Of course, you can write simpler methods, but this is the most fundamental version.
It’s the most fundamental (or nearly so) because the entire Web API framework is based on HttpMessageHandlers, as shown in the diagrams earlier.This is the signature of an HttpMessageHandler. The only difference is the requirement that the response be returned in a Task.You can certainly return a Task<HttpResponseMessage> from an ApiController, as well.However, you most likely won’t typically need a Task in your controller action, so we’ll generally stick to the Request -> Response signature.These two signatures will be our constant allies as we continue.
You are likely asking yourself, why focus on this one function?Your app needs to do a lot more than just serve HTTP requests with responses.NEXT: The Boundary
Your Web API project should be a boundary between your domain and your client.If you want to test your domain, test it before this boundary.Tests for the boundary should be testing the boundary itself.In other words, we want to isolate the boundary’s distribution mechanism.
If you unit test your controllers, you’ll often miss some important details such as correct status codes or various headers that you should expect or send.You’ll hit those when you work on integration testing, but by that point, you are likely focused on cross-cutting concerns like routing and auth.Discuss the pros/cons of using ApiController action selectors and model binders:Pros:GET requests can be simpler. URI parameters can be pulled in for you. Otherwise use UriTemplate.Other developers can determine expectations by reading the action method signatures.Can alleviate a line of code or two, such as not having to read the request body.Cons:In anything other than a GET, URI parameters are often only support for routing.The ApiController.Request property is not immediately visible.Reading the request body from Request.Content gives more control and is sometimes required anyway.Hides the fact that you are really dealing with a request/response interaction.From experience, you almost always have to return an HttpResponseMessage anyway to support various status codes.YMMV
Why the emphasis on functions?Functions are really easy to test.Functions can easily be composed.Functions can easily be wrapped.Functions generally help you avoid shared, mutable state, which in turn helps you avoid hard to detect bugs.These are all lessons from functional programming.Let’s test-drive this approach.
Rebuild the Post method using test-first/functional-first.Note that you can remove IoC use by means of closures.What about shared, mutable state?You always have that somewhere; just push it to the edges.Note: there are certainly limitations here.We cannot leverage the UrlHelper when testing only functions.That doesn’t mean you can’t use these.Think of function-first as an evolutionary testing approach. Build out from a simple function to include the rest of what you need as you go.
Eventually, you will likely want to evolve back to using HttpClient and HttpServer.Only HttpServer takes an HttpConfiguration, and you will need this for hosting your API on one of the out-of-the-box hosting models.Demo: use the valid Location header test as an example
We just saw an example of using this approach merely as a way of helping us focus on ensuring our request -> response interactions were correct.However, you don’t have to stop there.NEXT: ApiRouter
Show the GitHub API demo
Discuss how this is the approach taken by ApiRouter, though that eventually loads a controller.Why do we need controllers at all?What about functions composed together in such a way as to effectively route directly to the appropriate function?
Show the use of simple functions composed into a larger application
We covered testing in Web API.I showed you how you can better focus your testing so that you don’t let configuration distractions make you miss something in the request -> response interactions.We looked at a few ways to take these ideas further and then brought it all back together.Is this approach for everyone? Probably not.No project is the same; however, I have found that better focus on the separate components helps test code more accurately.
One thing I’ve been asked in the past relates to performance.I have yet to do any performance analysis on this technique as I’ve not had any performance problems.