What you have is very close. As Ryan mentioned, you need to move the controller class outside of the module and separate the client and server implementation details. There are a few other minor changes as well. Here's the updated code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
namespace SelfHost
(*
#r "System.Web.Http.SelfHost"       // AspNetMVC4Setup.exe Beta installed 
#r "System.Web.Http.Common"
#r "System.Web.Http"
#r "System.Net.Http.WebRequest"
#r "System.Net.Http.Formatting"
#r "System.Net.Http"
#r "System.Net"
*)
open System
open System.Text
open System.Web.Http
open System.Net.Http
open System.Web.Http.SelfHost 

type PersonController() =
    inherit ApiController()
        member x.Get() = "Person1"  // GET /api/person

module sh = 
    type aDefault = { id : obj }
  
    let Main args =
        let baseAddress = new Uri("http://localhost:8080/")
        let config = new HttpSelfHostConfiguration( baseAddress )
        config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", 
            {id = RouteParameter.Optional}) |> ignore
 
        // Create And Open Server
        let server = new HttpSelfHostServer(config)
        server.OpenAsync().Wait()
        Console.WriteLine("The server is running...")
 
        Console.ReadLine() |> ignore
 
    Main() 

You can then hit it from the browser with [link:localhost:8080]

By on 3/26/2012 6:56 PM ()

A special Thank-You goes to Ryan and Daniel, you saved my day!

So the main points are to have the controllers in separate modules! Which was not obvious to me.
And the controllers need a default constructor.
The client in my snippet is in the server module for testing only. Which works now as I expected, and also the browser access.

Btw. here [link:pfelix.wordpress.com] you can find nice stuff, also in-memory hosting as Ryan mentioned.

Problem solved
Thank you!

By on 3/27/2012 1:16 AM ()

FWIW, you need a ctor, not necessarily a default ctor. I totally missed that in your sample above as Daniel posted before I had a chance to run your code. (Nice catch, Daniel!)

Also, your constructor can't be inside any module, not just in a different module. I think that's what you meant, but I wanted to clarify. That's true at least for the DefaultHttpControllerFactory. If you use IoC or create your own IHttpControllerFactory, you can do anything you want. I've used that in my koans to work within FSI.

By on 3/27/2012 6:37 AM ()

Oh, another issue you will find in MVC (in general) is that placing controllers inside modules hides them from the DefaultControllerFactory/DefaultHttpControllerFactory. I have even had trouble with some IoC containers, though I'm not sure if all of them have the problem. It's related to the fact that your controller is inside a nested class. I don't know why that is an issue, but I've never been able to place controllers in modules and have them discovered.

In short, use a custom controller factory. :)

By on 3/26/2012 3:38 PM ()

I got the same error earlier. You are also using an ApiController. I hit this earlier and had to create a custom IHttpControllerFactory as the DefaultHttpControllerFactory was unable to find my controller either in FSI or my console application. My custom controller factory is available in the koans if you want to try to add that.

(Before I noticed the above, I had started writing this ...)

I haven't seen anyone try this exact approach before wrt calling the server from the client. I'll try it later tonight when I have a chance. The first things that jump out at me are:

1) I would expect this to work if you had separate console apps for your server and client.
2) HttpClient has a ctor overload that will take an HttpMessageHandler, of which HttSelfHostServer is a subclass, so you could construct your client as follows:

1
let client = new HttpClient(server)

3) I'm working on some koans to demonstrate how to build web api apps in F# at [link:github.com] It's early days, but feel free to create issues on things you'd like to see. I'll see if I can work this into my samples. I have one now that uses System.Web.Http.HttpServer (in-memory host) to do something very similar.

Ryan

By on 3/26/2012 3:30 PM ()

The link to the project didn't come through correctly. It's WebApiKoans. Again, it's still very early. More is coming.

By on 3/26/2012 9:59 PM ()

In VS, it's helpful to turn on break on all CLR exceptions from the Debug menu. Otherwise you see weird stuff like this in the response message with no idea what went wrong.

By on 3/26/2012 3:43 PM ()

What happens if you pass in an ID as well?

By on 3/25/2012 4:11 PM ()

Thank you for answering.
Passing an ID with "api/person/2" has the same effect.

I suppose the problem is in the routing mechanism.
I tried to connect via IE9 to "[link:localhost:8080] that showed "The webpage cannot be found".

Maybe the Web API convention-over-configuration will not work easily with F# generated type classes?

For that I've tried out a lot of different ApiController definitions without success -
like type PersonController () = // with default constructor
and type PersonController = // without default constructor
(I used ILSpy 2.0 to compare the compiled differences)
and with and without all the possible members Put(x), Post(x), Get(x), Get(), Delete(x)

What else can be wrong?

By on 3/26/2012 1:17 AM ()

Wow 388 views so far and no comment.

What does that mean,
- nobody has installed the ASP.Net Web API (Beta) to run the snippet code?
- it's running fine on other machines (no problem => no comments)?
- the post should be in an other forum?

Has anybody tried the snippet?

By on 3/25/2012 8:29 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper