【问题标题】:No HTTP resource was found that matches the request URI error in ASP.NET Web API未找到与 ASP.NET Web API 中的请求 URI 错误匹配的 HTTP 资源
【发布时间】:2014-04-11 13:08:54
【问题描述】:

这是我的 TransferController 类的草图。

所有这些都是 Web API 代码。

public class TransferController : ApiController
{
  [HttpGet, ActionName("Queue")]
  public IEnumerable<object> GetQueue(Guid sessionId) {...}

  [HttpDelete, ActionName("Delete")]
  public void Delete(Guid sessionId, Guid fileId) {...}

  [HttpGet, ActionName("Cancel")]
  public bool Cancel(Guid sessionId, Guid fileId) {...}

  [HttpGet, ActionName("UploadedBytes")]
  public long GetUploadedByteCount(Guid sessionId, Guid fileId) {...}

  [HttpGet, ActionName("DownloadUrl")]
  public string GetDownloadUrl(string fileId) {...}

  [HttpPost, ActionName("FileChunk")] 
  public void PostFileChunk([FromUri]Guid sessionId, [FromUri]Guid fileId) {...}

  [HttpPost, ActionName("UploadDefinition")]
  public Guid PostUploadItem([FromBody]UploadDefinition uploadDef) {...}

}

这是路由。

public static void Register(HttpConfiguration config)
{
  config.Routes.MapHttpRoute(
    name: "DefaultApi", 
    routeTemplate: "api/{controller}/{action}"
    );
  config.Routes.MapHttpRoute(
    name: "DefaultApiDefaultMethod", 
    routeTemplate: "api/{controller}"
    );
}

这是调用。

$.ajax({
  url: "api/Transfer/Queue",
  data: { sessiondId: login.SessionId() }    
})
.done(function (result) {
  history.push(new UploadItem());
  for (var i = 0; i < result.length; i++) {
    var ui = new UploadItem(result[i]);
    history.push(ui);
  }
})
.fail(function (result) {
  app.showMessage(JSON.parse(result.responseText).Message);
});

这就是结果。

No HTTP resource was found that matches the request URI 'http://localhost:54770/api/Transfer/Queue?sessiondId=0e2c47b9-e674-446d-a06c-ce16932f9580'.

这是我的 UserController 类的草图。

public class UserController : ApiController 

  [HttpGet, ActionName("Authenticate")]
  public object Authenticate(string email, string password) {...}

  [HttpPost]
  public void Register([FromBody]UserDefinition userDef) {...}

  [HttpGet, ActionName("Pulse")]
  public bool Pulse(Guid sessionId) {...}

}

由于我无法理解的原因,我在 UserController 中调用任何东西都没有问题。参数以完全相同的方式编组,并且 same 路由正在使用中。


下面的 Darrel Miller 使用单元测试来验证路由。坦率地说,我因为没有想到这一点而自责,现在我也这样做了。

但他展示的测试实际上只测试 URL 的解析。比如这个测试通过了

public void TestMvc4RouteWibble()
{
  var config = new HttpConfiguration();
  config.Routes.MapHttpRoute(
      name: "DefaultApi",
      routeTemplate: "api/{controller}/{action}/{id}",
      defaults: new { id = RouteParameter.Optional }
      );


  var route =
      config.Routes.GetRouteData(new HttpRequestMessage()
      {
        RequestUri = new Uri("http://localhost:54770/api/Transfer/Wibble?sessionId=0e2c47b9-e674-446d-a06c-ce16932f9580&fileId=0e2c47b9-e674-446d-a06c-ce16932f9581")  //?
      });

  Assert.IsNotNull(route);
  Assert.AreEqual("Transfer", route.Values["controller"]);
  Assert.AreEqual("Wibble", route.Values["action"]);

}

尽管 Transfer 控制器上明显没有 Wibble 方法。

另外,路由对象实际上并不是一个 HttpRoute 对象,它是一个 HttpRouteData 对象。但这是微不足道的纠正。 HttpRoute 对象可用作 HttpRouteData 对象的属性。

public void TestMvc4RouteWibble()
{
  var config = new HttpConfiguration();
  config.Routes.MapHttpRoute(
      name: "DefaultApi",
      routeTemplate: "api/{controller}/{action}/{id}",
      defaults: new { id = RouteParameter.Optional }
      );


  var routeData =
      config.Routes.GetRouteData(new HttpRequestMessage()
      {
        RequestUri = new Uri("http://localhost:54770/api/Transfer/Wibble?sessionId=0e2c47b9-e674-446d-a06c-ce16932f9580&fileId=0e2c47b9-e674-446d-a06c-ce16932f9581")  //?
      });

  Assert.IsNotNull(routeData);
  Assert.AreEqual("Transfer", routeData.Values["controller"]);
  Assert.AreEqual("Wibble", routeData.Values["action"]);

}

它又具有 Handler 属性。然而,这比它可能提供的信息更少,因为 null 处理程序只是意味着(来自 MSDN)

如果为 null,则默认处理程序将消息分派到 IHttpController 的实现。

现在,我的控制器是从 ApiController 派生的,它当然实现了 ExecuteAsync 方法,这是 IHttpController 接口指定的唯一方法。我想这意味着如果我了解更多,我可以测试该方法的执行。

【问题讨论】:

  • 我会避免获得Cancel
  • 不是所有动作都应该返回 ActionResult 吗?我不知道 Apicontroller,但在 Controller 中,您必须具有 Actionresult 或派生类型的返回类型...
  • 这是 Web API。从类继承自 ApiController 的事实来看,这一点应该很明显,但我会在问题中适当地指出它。
  • 有人会搜索“ASP.NET WEB API 路由难题”吗?您的标题应该是某人会搜索的内容。与论坛不同,这里的标题应该是对您的问题的描述。

标签: asp.net-web-api asp.net-web-api-routing


【解决方案1】:

这是一个证明路由工作正常的测试,

[Fact]
public void TestRoute() 
{
    var config = new HttpConfiguration();
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{action}"
        );


    var route =
        config.Routes.GetRouteData(new HttpRequestMessage()
        {
            RequestUri = new Uri("http://localhost:54770/api/Transfer/Queue?sessionId=0e2c47b9-e674-446d-a06c-ce16932f9580")  //?
        });

    Assert.NotNull(route);
    Assert.Equal("Transfer",route.Values["controller"]);
    Assert.Equal("Queue",route.Values["action"]);

}

这是一个测试,显示调度/动作选择也在起作用,

[Fact]
public void TestDispatch()
{
    var config = new HttpConfiguration();
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{action}"
        );

    var server = new HttpServer(config);

    var client = new HttpClient(server);
    var response =
        client.GetAsync(new Uri("http://localhost:54770/api/Transfer/Queue?sessionId=0e2c47b9-e674-446d-a06c-ce16932f9580")) // 
            .Result;

    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}


public class TransferController : ApiController
{
   [HttpGet]
   [ActionName("Queue")]
   public IEnumerable<object> Queue(Guid sessionId) 
   {
       return null;
   }

}

【讨论】:

  • 当然,我会试一试。但是叫我傻先生,为什么它对 api/User/Pulse 有效?
  • 将所有 Guid 更改为字符串并在方法内解析它们。对(我相信)路由失败的问题没有影响。
  • @PeterWone 我不知道为什么我之前会收到错误,但现在它对我来说工作正常......非常困惑。
  • 那个惹麻烦的东西现在工作正常,你是说 Guid 参数吗?
【解决方案2】:

好吧...感谢您将单元测试的想法放在我的脑海中,它极大地加快了速度。

这是内幕:

你可以在不同的动词上有相同的参数签名(get post put delete)。

不能在同一个动词的不同动作名称上具有相同的参数签名。

您只需要改变一个参数名称。

所以这没关系,因为它们都在不同的动词上

[HttpDelete, ActionName("Delete")]
public void Delete(Guid sessionId, Guid fileId) {...}

[HttpGet, ActionName("Cancel")]
public bool Cancel(Guid sessionId, Guid fileId) {...}

[HttpPost, ActionName("FileChunk")] 
public void PostFileChunk(Guid sessionId, Guid fileId) {...}

但这并不酷,因为它们都是gets

[HttpGet, ActionName("UploadedBytes")]
public long GetUploadedByteCount(Guid sessionId, Guid fileId) {...}

[HttpGet, ActionName("Cancel")]
public bool Cancel(Guid sessionId, Guid fileId) {...}

你可以这样修复它

[HttpGet, ActionName("UploadedBytes")]
public long GetUploadedByteCount(Guid sessionId, Guid uploadBytesFileId) {...}

[HttpGet, ActionName("Cancel")]
public bool Cancel(Guid sessionId, Guid cancelFileId) {...}

也许我是个硬汉,但就我而言,在调用方法之前它不会路由。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-17
    • 1970-01-01
    • 2014-07-17
    • 1970-01-01
    相关资源
    最近更新 更多