Frage

When I tried to use the Selfhosted WebAPI in LINQPad, I just kept getting the same error that a controller for the class didn't exist.

Do I have to create separate assemblies for the WebAPI (Controllers/Classes) and then reference them in my query?

Here's the code I'm using

#region namespaces
using AttributeRouting;
using AttributeRouting.Web.Http;
using AttributeRouting.Web.Http.SelfHost;
using System.Web.Http.SelfHost;
using System.Web.Http.Routing;
using System.Web.Http;
#endregion

public void Main()
{

    var config = new HttpSelfHostConfiguration("http://192.168.0.196:8181/");
    config.Routes.MapHttpAttributeRoutes(cfg =>
    {
        cfg.AddRoutesFromAssembly(Assembly.GetExecutingAssembly());
    });
    config.Routes.Cast<HttpRoute>().Dump();

    AllObjects.Add(new UserQuery.PlayerObject { Type = 1, BaseAddress = "Hej" });

    config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
    using(HttpSelfHostServer server = new HttpSelfHostServer(config))
    {
        server.OpenAsync().Wait();
        Console.WriteLine("Server open, press enter to quit");
        Console.ReadLine();
        server.CloseAsync();
    }

}

public static List<PlayerObject> AllObjects = new List<PlayerObject>();

public class PlayerObject
{
    public uint Type { get; set; }
    public string BaseAddress { get; set; }
}

[RoutePrefix("players")]
public class PlayerObjectController : System.Web.Http.ApiController
{
    [GET("allPlayers")]
    public IEnumerable<PlayerObject> GetAllPlayerObjects()
    {
        var players = (from p in AllObjects
                    where p.Type == 1
                    select p);
        return players.ToList();
    }
}

This code works fine when in a separate Console Project in VS2012.

I started using AttributeRouting via NuGET when I didn't get the "normal" WebAPI-routing to work.

The error I got in the browser was: No HTTP resource was found that matches the request URI 'http://192.168.0.196:8181/players/allPlayers'.

Additional error: No type was found that matches the controller named 'PlayerObject'

War es hilfreich?

Lösung

Web API by default will ignore controllers that are not public, and LinqPad classes are nested public, we had similar problem in scriptcs

You have to add a custom controller resolver, which will bypass that limitation, and allow you to discover controller types from the executing assembly manually.

This was actually fixed already (now Web API controllers only need to be Visible not public), but that happened in September and the latest stable version of self host is from August.

So, add this:

public class ControllerResolver: DefaultHttpControllerTypeResolver {

    public override ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver) {
        var types = Assembly.GetExecutingAssembly().GetExportedTypes();
        return types.Where(x => typeof(System.Web.Http.Controllers.IHttpController).IsAssignableFrom(x)).ToList();
    }

}

And then register against your configuration, and you're done:

var conf = new HttpSelfHostConfiguration(new Uri(address));
conf.Services.Replace(typeof(IHttpControllerTypeResolver), new ControllerResolver());

Here is a full working example, I just tested against LinqPad. Note that you have to be running LinqPad as admin, otherwise you won't be able to listen at a port.

public class TestController: System.Web.Http.ApiController {
    public string Get() {
        return "Hello world!";
    }
}

public class ControllerResolver: DefaultHttpControllerTypeResolver {
    public override ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver) {
        var types = Assembly.GetExecutingAssembly().GetExportedTypes();
        return types.Where(x => typeof(System.Web.Http.Controllers.IHttpController).IsAssignableFrom(x)).ToList();
    }
}

async Task Main() {
    var address = "http://localhost:8080";
    var conf = new HttpSelfHostConfiguration(new Uri(address));
    conf.Services.Replace(typeof(IHttpControllerTypeResolver), new ControllerResolver());

    conf.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

    var server = new HttpSelfHostServer(conf);
    await server.OpenAsync();

    // keep the query in the 'Running' state
    Util.KeepRunning();
    Util.Cleanup += async delegate {
        // shut down the server when the query's execution is canceled
        // (for example, the Cancel button is clicked)
        await server.CloseAsync();
    };
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top