在LINQPad中使用WebAPI?

17

当我尝试在LINQPad中使用Selfhosted WebAPI时,我一直得到同样的错误,即该类的控制器不存在。

我是否需要为WebAPI(控制器/类)创建单独的程序集,然后在我的查询中引用它们?

这是我正在使用的代码

#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();
    }
}

这段代码在VS2012的单独控制台项目中正常运行。

当我无法使“普通”的WebAPI路由工作时,我开始使用NuGET来使用AttributeRouting。

我在浏览器中收到的错误是:No HTTP resource was found that matches the request URI 'http://192.168.0.196:8181/players/allPlayers'.

额外的错误:No type was found that matches the controller named 'PlayerObject'


1
我使用您的代码(使用一些虚拟类/方法填充缺失的部分)生成了一个LINQ脚本,并添加了config.Routes.Cast<HttpRoute>().LogTo(Console.Out);。我确实看到路由URL: players/allPlayers GET, HEAD, OPTIONS。因此,这似乎表明路由已正确设置。在浏览器中,我收到“未找到与控制器名称'PlayerObject'匹配的类型。”但这似乎不同于您的错误(可能与我的虚拟类太虚拟有关)。 - Frank van Puffelen
可能反射类型是 System.Collections.Generic.IEnumerable``1[UserQuery+PlayerObject]?也许WebAPI无法将 UserQuery+PlayerObject 识别为普通的 PlayerObject 类? - NoLifeKing
噢,好吧,我可以使用VS来解决问题 :) 但是还是谢谢你抽出时间来帮忙 :) - NoLifeKing
@NoLifeKing,Filip W回答了你的问题吗? - Pete Klein
@PeteKlein,它确实解决了我的问题。只是忘记将其标记为已接受。谢谢提醒。 :) - NoLifeKing
显示剩余5条评论
1个回答

18

默认情况下,Web API 会忽略非公共的控制器,而 LinqPad 类是嵌套公共的,我们在 scriptcs 中也遇到了类似的问题。

您需要添加一个自定义控制器解析器,它将绕过此限制,并允许您手动从执行程序集中发现控制器类型。

实际上,这个问题已经得到了修复(现在 Web API 控制器只需要是可见的,而不是公共的),但这是在九月份发生的,而最新的稳定版自托管版本是从八月份开始的。

因此,请添加以下内容:

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();
    }

}

然后根据您的配置进行注册,完成后您就可以了:

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

以下是一个完整的工作示例,我刚刚在LinqPad上进行了测试。请注意,您必须以管理员身份运行LinqPad,否则您将无法监听端口。

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();
    };
}

13
有一个技巧可以强制LINQPad不嵌套类型:在查询开头加上#define NONEST。 - Joe Albahari
我有点困惑,这段代码应该放在哪里?我看了你的博客文章,试图弄清楚这段代码是作为“C#程序”放在LinqPad中,还是放在我的Web API应用程序中?或者两者都要放? - atconway
再来一个技巧,只需定义您的命名空间,就可以避免任何嵌套行为,无需任何技巧。 - VitalickS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接