Aucune ressource HTTP correspondant à l'erreur URI de la requête dans l'API Web ASP.NET n'a été trouvée

StackOverflow https://stackoverflow.com//questions/23013509

Question

Ceci est un croquis de ma classe TransferController.

Tout cela est du code de l'API Web.

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) {...}

}

C'est le routage.

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

C'est l'invocation.

$.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);
});

Et voici le résultat.

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

Ceci est un croquis de ma classe 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) {...}

}

Pour des raisons que je ne comprends pas, je n'ai aucune difficulté à appeler quoi que ce soit dans le UserController.Les paramètres sont organisés exactement de la même manière, et le même les itinéraires sont utilisés.


Darrel Miller ci-dessous utilise des tests unitaires pour valider les itinéraires.Franchement, je m'en veux de ne pas y avoir pensé, et maintenant j'ai fait la même chose.

Mais les tests tels qu'il les montre ne testent en réalité que l'analyse de l'URL.Par exemple, ce test réussit

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"]);

}

malgré l'absence flagrante de méthode Wibble sur le contrôleur de transfert.

De plus, l'objet route n'est pas réellement un objet HttpRoute, c'est un objet HttpRouteData.Mais c'est trivialement corrigé.L'objet HttpRoute est disponible en tant que propriété de l'objet 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"]);

}

Et il possède à son tour une propriété Handler.Cependant, ceci est moins informatif qu'il ne pourrait l'être, puisqu'un gestionnaire nul signifie simplement (depuis MSDN)

S'il est nul, le gestionnaire par défaut distribue des messages aux implémentations de IHttpController.

Maintenant, mon contrôleur est dérivé d'ApiController qui implémente certainement la méthode ExecuteAsync qui est la seule chose spécifiée par l'interface IHttpController.Ce qui, j'imagine, signifie que je pourrais tester l'exécution de cette méthode si j'en savais plus.

Était-ce utile?

La solution 2

Alors ok...merci d'avoir mis l'idée du test unitaire dans ma tête, cela a énormément accéléré les choses.

Voici la vérité :

Vous pouvez avoir des signatures de paramètres identiques sur différents verbes (get post put delete).

Toi ne peut pas avoir des signatures de paramètres identiques sur différents noms d'action sur le même verbe.

Il vous suffit de varier un le nom du paramètre.

Donc ça va parce qu'ils sont tous sur des verbes différents

[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) {...}

mais ce n'est pas cool parce qu'ils sont tous les deux

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

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

et tu peux le réparer comme ça

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

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

Peut-être que je suis un dur à cuire, mais en ce qui me concerne, il n'y a pas de routage jusqu'à ce que la méthode soit appelée.

Autres conseils

Voici un test qui démontre que le routage fonctionne correctement,

[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"]);

}

et voici un test montrant que la sélection répartition/action fonctionne également,

[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;
   }

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top