Skip to content

Instantly share code, notes, and snippets.

@panesofglass
Created June 21, 2012 16:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save panesofglass/2966916 to your computer and use it in GitHub Desktop.
Save panesofglass/2966916 to your computer and use it in GitHub Desktop.
ApiController in F# Interactive
#I @"..\packages\Microsoft.Net.Http.2.0.20505.0\lib\net40"
#I @"..\packages\Microsoft.AspNet.WebApi.Core.4.0.20505.0\lib\net40"
#I @"..\packages\Microsoft.AspNet.WebApi.Client.4.0.20505.0\lib\net40"
#I @"..\packages\ImpromptuInterface.5.6.7\lib\net40"
#I @"..\packages\ImpromptuInterface.FSharp.1.1.0\lib\net40"
#I @"..\packages\Newtonsoft.Json.4.5.6\lib\net40"
#I @"..\packages\Unquote.2.2.1\lib\net40"
#r "System.Net.Http.dll"
#r "System.Net.Http.Formatting.dll"
#r "System.Web.Http.dll"
#r "ImpromptuInterface.dll"
#r "ImpromptuInterface.FSharp.dll"
#r "Newtonsoft.Json.dll"
#r "Unquote.dll"
open System
open System.Collections.Generic
open System.Net
open System.Net.Http
open System.Threading.Tasks
open System.Web.Http
open System.Web.Http.Controllers
open System.Web.Http.Dispatcher
// NOTE: This is a post-RC update that will be available in the RTM bits.
// http://aspnetwebstack.codeplex.com/SourceControl/changeset/changes/07f8232f33af
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
/// <summary>
/// Provides an implementation of <see cref="IAssembliesResolver"/> with no external dependencies.
/// </summary>
type AssembliesResolver() =
interface IAssembliesResolver with
/// <summary>
/// Returns a list of assemblies available for the application.
/// </summary>
/// <returns>A <see cref="Collection{T}"/> of assemblies.</returns>
member x.GetAssemblies() =
let assemblies = new List<_>(AppDomain.CurrentDomain.GetAssemblies())
assemblies.Add(System.Reflection.Assembly.GetExecutingAssembly())
assemblies :> ICollection<_>
// NOTE: This is a post-RC update that will be available in the RTM bits.
// http://aspnetwebstack.codeplex.com/SourceControl/changeset/changes/07f8232f33af
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
/// <summary>
/// Provides an implementation of <see cref="IHttpControllerTypeResolver"/> with no external dependencies.
/// </summary>
type HttpControllerTypeResolver (?predicate) =
let _isControllerTypePredicate = defaultArg predicate HttpControllerTypeResolver.IsControllerType
static member IsControllerType (t: Type) =
System.Diagnostics.Contracts.Contract.Assert(t <> null)
t <> null
&& t.IsClass
&& t.IsVisible
&& t.Name.EndsWith(DefaultHttpControllerSelector.ControllerSuffix, StringComparison.OrdinalIgnoreCase)
&& not t.IsAbstract
&& typeof<IHttpController>.IsAssignableFrom(t);
interface IHttpControllerTypeResolver with
/// <summary>
/// Returns a list of controllers available for the application.
/// </summary>
/// <returns>An <see cref="ICollection{Type}"/> of controllers.</returns>
[<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Catching all exceptions in this case is the right to do.")>]
member x.GetControllerTypes (assembliesResolver: IAssembliesResolver) =
if assembliesResolver = null then
raise <| new ArgumentNullException("assembliesResolver")
let result = new List<Type>()
// Go through all assemblies referenced by the application and search for types matching a predicate
let assemblies = assembliesResolver.GetAssemblies()
for assembly in assemblies |> Seq.filter (fun a -> a <> null && not a.IsDynamic) do
let mutable exportedTypes : Type[] = null
try
exportedTypes <- assembly.GetExportedTypes()
with
| :? System.Reflection.ReflectionTypeLoadException as ex -> exportedTypes <- ex.Types
| _ -> ()
// We deliberately ignore all exceptions when building the cache. If
// a controller type is not found then we will respond later with a 404.
// However, until then we don't know whether an exception at all will
// have an impact on finding a controller.
if exportedTypes <> null then
result.AddRange(exportedTypes |> Array.filter _isControllerTypePredicate)
result :> ICollection<_>
module Sandbox =
type HttpRouteDefaults = { id : RouteParameter }
type ValuesController() =
inherit ApiController()
member x.GetAll() = [|"value1";"value2"|]
member x.Get(id: int) = "value"
member x.Post([<FromBody>] value: string) = x.Request.CreateResponse(HttpStatusCode.Created, value)
member x.Put(id: int, [<FromBody>] value: string) = ()
member x.Delete(id: int) = ()
let run() =
let cts = new System.Threading.CancellationTokenSource()
use config = new HttpConfiguration()
config.Services.Replace(typeof<IAssembliesResolver>, new AssembliesResolver())
config.Services.Replace(typeof<IHttpControllerTypeResolver>, new HttpControllerTypeResolver())
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", { id = RouteParameter.Optional }) |> ignore
use server = new HttpServer(config)
use client = new HttpMessageInvoker(server)
use request = new HttpRequestMessage(HttpMethod.Get, "http://sandbox/api/values")
async {
use! response = Async.AwaitTask <| client.SendAsync(request, cts.Token)
printfn "Response: %A" response
if response.Content <> null then
let! content = Async.AwaitTask <| response.Content.ReadAsStringAsync()
printfn "Content: %A" content
} |> Async.RunSynchronously
Sandbox.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment