WebAPI and custom model formatters

Just had this little gotcha:

I needed to do some operations on input parameter to a POST method, so wrote a custom formatter, and added it to a list:

config.Formatters.Add(new MyCustomFormatter());

I’m sure those who have done it already see the problem:) Basically web api has a catch-all formatters, so if you do it like this – your formatter will never be called.

This will work:

config.Formatters.Insert(0, new MyCustomFormatter());

Note that this is valid for cases when you’re changing input model, if you want to modify it in response – the .Add method works.

CORS and IIS

I’ve been using the filter I wrote about in previous blog entry in dev for a while, and it worked fine. So I’m pushing the app to the IIS, and it stops working – fails on option request.

Weird thing was the response wasn’t what I was expecting, and when attaching debugger to IIS process – it was clear that filter wasn’t firing.

 

My request headers were along these lines:

 

Request URL:http://www.mydomain.com/api/v1/Application

Request Method:OPTIONS

Status Code:200 OK

Request Headersview source

Accept:*/*

Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3

Accept-Encoding:gzip,deflate,sdch

Accept-Language:en-US,en;q=0.8

Access-Control-Request-Headers:origin, x-requested-with, accept

Access-Control-Request-Method:GET

 

And response was this:

 

Response Headersview source

Allow:OPTIONS, TRACE, GET, HEAD, POST

Content-Length:0

Date:Wed, 19 Dec 2012 06:58:02 GMT

Public:OPTIONS, TRACE, GET, HEAD, POST

Server:Microsoft-IIS/8.0

X-Powered-By:ASP.NET

 

Obviously not something my filter would return, so I went to IIS to see if something would handle the OPTIONS request before it gets to the application, and found an OPTIONSVerbHandler in Handler Mappings. This returns.. well, see above, which is not what I want – so deleted it from the site, and boom – it worked.

WebApi and cross-origin requests (CORS)

This took me a while to figure out.

Was trying to access my web api service from another domain using angular, and it just wasn’t working, even when I added Access-Control-Allow-Origin header to the response. Interestingly enough ajax request with jQuery worked.

Anyways, here’s the fix:

1. You will need to send a few headers back, to allow the origin, methods, and maybe a few custom headers. This you can do in an action filter (or you can do it in action itself, or add it to custom headers in web.config, or you an write an http handler.. but this is DRY and easy and gives you control over which methods/controllers will be accessible without too much fuss).

 

2. Browser will likely make a pre-flight OPTIONS request, so you need.. guess what.. an OPTIONS method on you controller. This method doesn’t have to do anything or return anything, but it has to be there.

And it works.. simple enough, but damn it was quite a bit of googling to understand what’s going on.

 

Experiment: stock management using WebApi, TypeScript, angular.js, RavenDB, and a bit of sugar.js on top (and maybe even OData). Part II, client side continued

OK, to the service (angular service).

This is the piece that actually makes calls to back end.

Again, it’s pretty straight forward, if you’ve used angular before, just slight variations on the syntax.

However, few things to note here:

If you’ll look at Update method, this
var provider = this.resource(“/api/Product”, {}, { update: { method: ‘PUT’ } });
specifies a new method on provider, and tells it to use HTTP PUT, but for this to work you’ll need to make a change in d.ts file. So open angular-resource-1.0.d.ts, find interface IResourceClass, and add update: IActionCall; line to it. Good.

Also, there’s a catch with RavenDB and methods asking for an id. Raven’s ids by default look like “Product/123“, so when you’re making a GET/DELETE requests that look like DELETE http://mysite.com/api/product/Product/123 – MVC gets confused. If you just send a whole object, though, it figures it out just fine.. or you can actually specify an id as a url parameter, like this http://mysite.com/api/product?id=Product/123. You can always supply your own Id, of course, for example make it a GUID, or play with Raven’s DocumentKeyGenerator.

 

Almost done.

So now to the html, views, and directives.

index.html file looks like this

Pretty basic container, which requires a view module, specifying the routes and controllers:

And a last thing, a directive that displays a message.

Now this uses a pretty interesting mechanism to specify message to display.

First, this is how it’s called from html:

and this is the directive’s html, so that it makes more sense:

see the  scope: { messageData: ‘=messageData’ } line in directive? It basically says to create a new local scope for this directive, and create a messageData variable in it. The content of this variable will be taken from a parent scope’s property, and that is defined by  data-message-data=“OperationResultMessage” piece in calling code. So, in short, using this code, an OperationResultMessage property from parent scope is bound to the messageData property of a directive’s scope, and the binding it two-ways.

 

OK, I think that’s mostly it. If I missed anything – ask.

Again, the complete solution is on GitHub.

Experiment: stock management using WebApi, TypeScript, angular.js, RavenDB, and a bit of sugar.js on top (and maybe even OData). Part II, client side

OK, to the client part. This is going to be a long post…
Word of advice – install Web Essentials extension in Visual Studio – this compiles a typescript (.ts) file into a js on save, and opens a 2nd windows so that you can see right away the generated js code.. it helps.
To get a static typing for angular (and sugar.js) I’ve used type definitions taken from here. There are others available, but these work pretty well, and have decent documentation (even though they need some fixin.. but more on this later).

This is how the result looks.

Done with Twitter Bootstrap. Didn’t really bother making it pretty, that’s not the point.
I have an index file, serving as a container, and 3 views – for products, categories, and stores.

So here we have a list of products, a list of categories, a set of buttons, 2 filters, and a popup message. Pretty basic.
Categories tab looks pretty much the same.

On this screen I want to be able to do the whole CRUD, I want the buttons to be disabled until it makes sense to enable them (that is user entered proper data), and I want the popup message, telling me if operation succeeded or failed.

So on the client side I have an interface for angular $scope, controller, service, and a product model. I’m also using category model, and a directive to display the operation result messages.

Scope interface.

This is basically just a definition of properties/methods I will have there.. well, it’s an interface. This serves no other purpose but to have static checking – that is this interface itself is not reflected in resulted JS file in any way.

The actual implementation will happen in controller.

 

Ugh, that’s a long listing.
So here I’m setting up a scope variables and methods, adding a watch to change a Create/Update button’s text, and otherwise it’s mostly calls to the service.
Things to keep in mind:
1. This is big. TypeScript compiles to JavaScript. The static typing is compile-time only, it is not enforced at run time. This is not a C#. Even if you’re saying that your function takes a parameter with type of MyClass, it will not be enforced at run time – your function can be called with no parameters, or wrong parameters.. Well, usual JS stuff.
2. For reason above the naming conventions of angular are still applied – don’t expect to set a scope variable to ng.IScope, and expect it to work – it still have to be named $scope.

OK, I think I’ll make another post here, it’s getting too long.