This is a guide to performing dependency injection on a WebAPI ActionFilter. Specifically, an ActionFilter that implements System.Web.Http.Filters.ActionFilterAttribute, as this is designed to work on classes implementing ApiController and the methods of those classes. The IoC container library in this case is StructureMap.
Firstly, define your ActionFilter with its dependencies as properties with public setters. In this example our ActionFilter has a dependency on RavenDB's IDocumentStore. The purpose of the ActionFilter is to apply Aggressive Caching to the IDocumentStore.
We then need to write a custom IFilterProvider (again, of the System.Web.Http.Filters namespace) in order to provide us with ActionFilters with fully resolved dependencies. Our IFilterProvider uses the StructureMap container to 'build up' the ActionFilter's property dependencies via their public setter.
Our custom IFilterProvider also implements ActionDescriptorFilterProvider. This is the default IFilterProvider that asp.net will use if none other is specified. It is necessary because we need access to the GetFilters() method to return us the instantiated ActionFilters needed for the request. We then take each instantiated ActionFilter and pass it to StructureMap to inject the dependencies.
We then configure StructureMap to use our implementation of IFilterProvider when an instance is needed to create ActionFilters. What's interesting here is that we are using StructureMap to provide us with an implementation of something that will then do further dependency resolution for us.
Lastly, we also need to tell StructureMap which property types it should be looking to resolve dependencies for with public setters. All this configuration lives in the single StructureMap initialization step, but it is arguable that this should be done in the originating interface assembly IoC configuration.
Then we are able to decorate any ApiController implementation class or method with our AggresivelyCacheFor ActionFilter wherever we need Aggressive Caching on our IDocumentStore Happy ActionFiltering!
Showing posts with label mvc4. Show all posts
Showing posts with label mvc4. Show all posts
Wednesday, 7 November 2012
Monday, 6 August 2012
Specflow BDD test with HttpClient
I've been using Specflow for a while now for various BDD testing scenarios. However until now I'd not come across an example that I'd like to blog about.
Enter: HttpClient and HttpRequestMessage included in .net 4.5 under the System.Net.Http namespace.
Straight forward enough, but the complexity arises because we want a single Visit Tracking Utility that spans multiple sites, each on a different platform and written in different languages. Some are even 3rd party sites, so we have minimal control over the html displayed - and zero control over whats going on at the server. In order to achieve this platform-agnostic solution we need to solve the problem with javascript as we accept will have at least this much control over each target site. We also have some specification about the name and value of the cookie as well as the expiry date date.
The technical requirement therefore is a url available to make requests to that will drop a cookie on the clients machine called VisitId, with a Guid value that expires in 7 days time.
Starting with a new IntegrationTest project in the solution we add a new VisitTracking feature file and enter the following scernario.
Each 'Then' and following 'And' gives a chance to make an assertion about the request we have made. We are testing exactly the things described in our specification.
If we build and try to run this now, then specflow will tell us that we are missing the steps specified in the scenario:
Copy from the test output all the necessary steps into a VisitTrackingSteps class. With a couple of extra helper methods and paramter arguments on your class should look like the following. Don't forget to use the [Binding] attribute before the class declaration or specflow won't know where to look for the steps. Also you will need to install the System.Net.Http package to get reference to the HttpClient object et al.
You can see that in the 'When' method the HttpClient and HttpRequestMessage are used to initiate the call to the visit tracking site - at present not yet created. The SendAsync method of the HttpClient is called using a GET request to the path of the api controller method. This is replicating exactly what the script tag will do from each web page it appears on. So we have been able to quickly conjure up a http request without the use of selenium or other web driver. The other nice part is that we can interrogate the response from the request by accessing the Result property of the generic Task we get from calling SendASync.
The tests will build and run now but there is currently nothing to hit so the tests will fail. We can now implement the logic that will fulfil the spec.
The unit tests are already fathomable for this method, but this post is about integration testing, so thats what we'll focus on,
All the source is available in my GitHub Repository. And you will have to setup the site locally and adjust the host binding yourself if you want this to run out-of-the-box :-).
Enter: HttpClient and HttpRequestMessage included in .net 4.5 under the System.Net.Http namespace.
Scenario
The story starts with the requirement to drop a client cookie so we know which users have visited the site before. And perform some basic logging about the visit details, query string information, referrer etc.Straight forward enough, but the complexity arises because we want a single Visit Tracking Utility that spans multiple sites, each on a different platform and written in different languages. Some are even 3rd party sites, so we have minimal control over the html displayed - and zero control over whats going on at the server. In order to achieve this platform-agnostic solution we need to solve the problem with javascript as we accept will have at least this much control over each target site. We also have some specification about the name and value of the cookie as well as the expiry date date.
The technical requirement therefore is a url available to make requests to that will drop a cookie on the clients machine called VisitId, with a Guid value that expires in 7 days time.
The BDD Test
If you are not already, please familiarise with specflow. The plan here is to capture the Behaviour in a Feature file that we can make assertions from based on our code. We begin by citing the scenario we want to test using the Gherkin Given; When; Then format. Specflow then allows us to attach code to the steps we dictate, then we insert the required code and necessary NUnit assertions to validate our scenario.Starting with a new IntegrationTest project in the solution we add a new VisitTracking feature file and enter the following scernario.
Feature: Visit Tracking
In order to know which users have previously visited
I want to drop a client cookie on each visit
Scenario: Setting client cookie for a new visit
Given the api uri is local.trackmyvisit.com/api/trackvisit
And the expected cookie name is VisitId
When I hit the visit tracking uri
Then the response HttpCode is OK
And the response sets a cookie
And the cookie name is correct
And the cookie value is a valid Guid
And the cookie expiry is 7 days from now
Each 'Then' and following 'And' gives a chance to make an assertion about the request we have made. We are testing exactly the things described in our specification.
If we build and try to run this now, then specflow will tell us that we are missing the steps specified in the scenario:
-> No matching step definition found for the step. Use the following code to create one:
[When(@"I hit the visit tracking uri")]
public void WhenIHitTheVisitTrackingUri()
{
ScenarioContext.Current.Pending();
}
Copy from the test output all the necessary steps into a VisitTrackingSteps class. With a couple of extra helper methods and paramter arguments on your class should look like the following. Don't forget to use the [Binding] attribute before the class declaration or specflow won't know where to look for the steps. Also you will need to install the System.Net.Http package to get reference to the HttpClient object et al.
[Binding]
public class VisitTrackingSteps
{
private string _uri;
private string _cookieName;
private HttpResponseMessage _result;
[Given(@"the api uri is (.*)")]
public void GivenTheApiUriIs(string uri)
{
_uri = "http://" + uri;
}
[Given(@"the expected cookie name is (.*)")]
public void GivenTheExpectedCookieNameIs(string cookieName)
{
_cookieName = cookieName;
}
[When(@"I hit the visit tracking uri")]
public void WhenIHitTheVisitTrackingUri()
{
var client = new HttpClient();
var msg = new HttpRequestMessage(HttpMethod.Get, _uri);
_result = client.SendAsync(msg).Result;
}
[Then(@"the response HttpCode is (.*)")]
public void ThenTheResponseHttpCodeIs(HttpStatusCode statusCode)
{
Assert.That(_result.StatusCode, Is.EqualTo(statusCode));
}
[Then(@"the response sets a cookie")]
public void ThenTheResponseSetsACookie()
{
var isSetCookieHeaderPresent =
!String.IsNullOrEmpty(GetValueOfSetCookieHeader());
Assert.IsTrue(isSetCookieHeaderPresent);
}
[Then(@"the cookie name is correct")]
public void ThenTheCookieNameIsCorrect()
{
var firstKey = ParseSetCookieValue().AllKeys.FirstOrDefault();
Assert.That(firstKey, Is.EqualTo(_cookieName));
}
[Then(@"the cookie value is a valid Guid")]
public void ThenTheCookieValueIsAValidGuid()
{
Guid guid;
var guidValue = ParseSetCookieValue()[_cookieName];
var isValidGuid = Guid.TryParse(guidValue, out guid);
Assert.IsTrue(isValidGuid);
}
[Then(@"the cookie expiry is (.*) days from now")]
public void ThenTheCookieExpiryIsDaysFromNow(int days)
{
var expiresValue = ParseSetCookieValue()["expires"];
var expires = Convert.ToDateTime(expiresValue);
var rangeStart = DateTime.Now.AddDays(7).AddMinutes(-1);
var rangeEnd = DateTime.Now.AddDays(7).AddMinutes(1);
Assert.That(expires, Is.InRange(rangeStart, rangeEnd));
}
private string GetValueOfSetCookieHeader()
{
IEnumerable<string> values;
_result.Headers.TryGetValues("Set-Cookie", out values);
return values.FirstOrDefault();
}
private NameValueCollection ParseSetCookieValue()
{
var collection = new NameValueCollection();
var cookieValArray = GetValueOfSetCookieHeader().Split(';');
foreach (var arr in cookieValArray.Select(s => s.Split('=')))
{
collection.Add(arr[0].Trim(), arr[1].Trim());
}
return collection;
}
}
You can see that in the 'When' method the HttpClient and HttpRequestMessage are used to initiate the call to the visit tracking site - at present not yet created. The SendAsync method of the HttpClient is called using a GET request to the path of the api controller method. This is replicating exactly what the script tag will do from each web page it appears on. So we have been able to quickly conjure up a http request without the use of selenium or other web driver. The other nice part is that we can interrogate the response from the request by accessing the Result property of the generic Task we get from calling SendASync.
The tests will build and run now but there is currently nothing to hit so the tests will fail. We can now implement the logic that will fulfil the spec.
Tracking Logic using an Api Controller
Our spec tells us we need a uri we can hit that will perform the visit tracking logic and write back a cookie to the client through the http response. Furthermore, as there will be now javascript or html returned in the response (i.e. nothing for the client to parse) this sounds like a candidate for using an MVC4 WebApi Controller. The controller implements the exact requirements of the spec.
public class TrackVisitController : ApiController
{
// call using GET method to /api/trackvisit
public HttpResponseMessage Get()
{
var cookie = new HttpCookie("VisitId")
{
Value = Guid.NewGuid().ToString(),
Expires = DateTime.Now.AddDays(7)
};
HttpContext.Current.Response.Cookies.Add(cookie);
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
The unit tests are already fathomable for this method, but this post is about integration testing, so thats what we'll focus on,
Script Tag
We then only need to tag each page we wish to track. The uri is used for the source of a script tag on each page we want our tracking system to work on.<script src="http://test.trackmyvisit.com/api/trackvisit" />
All the source is available in my GitHub Repository. And you will have to setup the site locally and adjust the host binding yourself if you want this to run out-of-the-box :-).
Labels:
bdd,
cookie,
github,
httpclient,
httprequestmessage,
mvc4,
specflow,
webapi
Monday, 30 July 2012
Config Settings into JavaScript using IBundleTransforms
The Problem:
You need to dynamically set a variable in a static JavaScript file.
The variable changes with each target environment (test/production etc) and so needs to be set in the app settings (web.config).
Bundling:
MVC4 comes with out of the box bundling capability for static text content. Read for what bundling is and why it is a good idea.
The .net Bundling feature has been designed to be pluggable so you can roll your own Bundler by implementing the IBundleTransform interface.
This is very neat way of addressing the problem of setting a variable in a static file, as you can handle the logic of replacement in your own IBundleTransform class.
The Solution:
1. Add the configuration variable to your app settings of your web.config.
2. Replace the value of the variable being setting in the script with a token e.g. {{ServerAddress}}.
3. Create a bundle Transform that looks for this token and replaces it with the setting from the app settings. We are dealing with the BundleResponse object here.
4. Initialise the bundle in the RegisterBundles method of your BundleConfig in the App_Start folder, specify the virtual path, the files and folders you want to be bundled. Then add a new instance of your IBundleTransform to the list of the bundle's Transforms.
Now, when you make a request to the virtual path (~/Scripts/AllScripts.js) your JavaScript will be returned both minified and with the configuration specific variable included! The replacement logic only gets called once too, as .net will cache the output.
What's more, the MyBundleTransform class is nicely contained and Unit Testable.
Hope this helps!
Thursday, 12 July 2012
Method Not Allowed 405 on IIS7 Website eg PUT, DELETE etc
If you are working with a .net MVC4 WebApi project that needs to provide CRUD capabilities then your the standard Http Methods your site will need to accept are GET, POST, PUT, DELETE.
I had trouble getting my site to accept PUT and DELETE methods when hosting my site locally with IIS7. I am hosting the application under its own website, this lets me change the settings for the particular site using the IIS7 interface. Here are the steps I took to correct the problem:
Begin in IIS7 by highlighting your website, then choosing Handler Mappings from the available options:
I had trouble getting my site to accept PUT and DELETE methods when hosting my site locally with IIS7. I am hosting the application under its own website, this lets me change the settings for the particular site using the IIS7 interface. Here are the steps I took to correct the problem:
Begin in IIS7 by highlighting your website, then choosing Handler Mappings from the available options:
Then select WebDAV from the list of Mappings.
Click Request Restrictions.
Then switch to the Verbs tab and highlight All Verbs.
Restart the application in IIS and hey presto! PUT and DELETE enabled (as well as all HTTP methods). If you want to more conservative about which methods are supported then use the option beneath All Verbs.This results in the changes to your Web.config. Inside your <system.web> section goes this snippet: (You don't actually have to add this - IIS7 will have added this itself)
<handlers>
<remove name="WebDAV" />
<add name="WebDAV" path="*" verb="*" modules="WebDAVModule" resourceType="Unspecified" requireAccess="None" />
</handlers>
Hope this will help someone out!
P.s. I found WFetch to be quite a useful tool in debugging http requests.
Labels:
405,
delete,
http method,
iis7,
mvc4,
not allowed,
put,
webapi
Subscribe to:
Posts (Atom)


