Sono state trovate più azioni che corrispondono alla richiesta in API Web


243

Continuo a ricevere questo errore quando provo ad avere 2 metodi "Get"

Sono state trovate più azioni che corrispondono alla richiesta: webapi

Ho guardato in giro le altre domande simili su questo in pila ma non capisco.

Ho 2 nomi diversi e utilizzo l'attributo "HttpGet"

[HttpGet]
public HttpResponseMessage Summary(MyVm vm)
{
    return null;
}

[HttpGet]
public HttpResponseMessage FullDetails()
{
    return null;
}

Risposte:


485

La tua mappa del percorso è probabilmente qualcosa del genere:

routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });

Ma per avere più azioni con lo stesso metodo http è necessario fornire a webapi ulteriori informazioni sul percorso in questo modo:

routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });

Si noti che routeTemplate ora include un'azione. Molte più informazioni qui: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

Aggiornare:

Bene, ora che penso di capire cosa stai cercando qui è un'altra interpretazione di questo:

Forse non hai bisogno del parametro action url e dovresti descrivere i contenuti che stai cercando in un altro modo. Dal momento che stai dicendo che i metodi stanno restituendo dati dalla stessa entità, lascia che i parametri facciano la descrizione per te.

Ad esempio i tuoi due metodi potrebbero essere trasformati in:

public HttpResponseMessage Get()
{
    return null;
}

public HttpResponseMessage Get(MyVm vm)
{
    return null;
}

Che tipo di dati stai passando nell'oggetto MyVm? Se riesci a passare semplicemente le variabili attraverso l'URI, suggerirei di seguire questa strada. Altrimenti, dovrai inviare l'oggetto nel corpo della richiesta e non è molto HTTP da parte tua quando fai un GET (funziona però, usa [FromBody] davanti a MyVm).

Si spera che questo illustri che puoi avere più metodi GET in un singolo controller senza usare il nome dell'azione o anche l'attributo [HttpGet].


Ci sono dei vantaggi nel fare in un modo o nell'altro? Se faccio il secondario, devo solo mettere l'azione Http su ciascun metodo? È questo il grande limite?
chobo2

3
Se uno ha un vantaggio rispetto all'altro dipende davvero dal tuo progetto. Se stai creando un'API RESTful, ti consigliamo di utilizzare le convenzioni HTTP (GET, POST, PUT, DELETE ...). In questo caso, il primo blocco di codice di routing è la strada da percorrere, ma avrai bisogno di un controller diverso per ogni entità che esponi attraverso l'API. Sulla base dei nomi dei tuoi metodi, immagino che non sia così, quindi usa il routing più descrittivo. Quando il percorso include l'azione, dovrai inserire esplicitamente l'attributo http su ciascun metodo.
Jed

1
@ chobo2 Perché non usare solo metodi che sono nominati di conseguenza all'interno del controller? GetSummary (MyVm wm) e GetDetails ()
Jed,

1
Grazie per la tua risposta, mi ha aiutato a capire perché la risoluzione del percorso non funzionava anche se entrambe le mie azioni avevano nomi diversi. Sono davvero confuso sul perché non sia solo il comportamento predefinito (ovvero perché il modello di route predefinito in webapiconfig.cs non includa "{action}")!
Tejas Sharma,

1
@bruno se si utilizza aree è anche possibile aggiungere le API 'admin' specifici come questo in AdminAreaRegistration stackoverflow.com/a/9849011/16940
Simon_Weaver

67

Aggiornamento dall'API Web 2.

Con questa configurazione API nel file WebApiConfig.cs:

public static void Register(HttpConfiguration config)
{
    //// Web API routes
    config.MapHttpAttributeRoutes(); //Don't miss this

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

Puoi indirizzare il nostro controller in questo modo:

[Route("api/ControllerName/Summary")]
[HttpGet]
public HttpResponseMessage Summary(MyVm vm)
{
    rturn null;
}

[Route("api/ControllerName/FullDetails")]
[HttpGet]
public HttpResponseMessage FullDetails()
{
    return null;
}

Dove ControllerName è il nome del controller (senza "controller"). Ciò ti consentirà di ottenere ogni azione con il percorso sopra descritto.

Per ulteriori letture: http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2


Mi è davvero piaciuta questa soluzione. Il mio percorso predefinito è sempre lo stesso e ho un percorso "eccezione" per le eccezioni
Leandro De Mello Fagundes

puoi anche mappare i parametri nell'URL EX: [Route ("api / ControllerName / Summary / {vm}")]
nulltron

15

Nell'API Web (per impostazione predefinita) i metodi vengono scelti in base a una combinazione di metodo HTTP e valori di route .

MyVm sembra un oggetto complesso, letto dal formattatore dal corpo in modo da avere due metodi identici in termini di dati di percorso (poiché nessuno di essi ha parametri dal percorso) - il che rende impossibile per il dispatcher (IHttpActionSelector ) di abbinare quello appropriato .

È necessario differenziarli per querystring o parametro di route per risolvere l'ambiguità.


14

Dopo molte ricerche sul Web e tentando di trovare il modulo più adatto per il routing map, se hai trovato quanto segue

config.Routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id =RouteParameter.Optional }, new { id = @"\d+" });
config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");

Queste mappature si applicano sia alla mappatura dei nomi delle azioni sia alla convenzione http di base (GET, POST, PUT, DELETE)


9
Per me questo ha funzionato, ma solo dopo aver cambiato l'ordine dei percorsi nella configurazione del percorso in modo che quello con l'azione apparisse per primo
Fredrik Stolpe,

esattamente l'ordine è importante qui
AL


5

Senza usare le azioni le opzioni sarebbero:

  1. sposta uno dei metodi su un controller diverso, in modo che non si scontrino.

  2. usa solo un metodo che accetta il parametro e, se è nullo, chiama l'altro metodo dal tuo codice.


Potrebbe essere una soluzione, ma non ottimale, comunque +1 dalla mia parte :)
satish kumar V

4

Questa soluzione ha funzionato per me.

Inserire prima Route2 in WebApiConfig. Aggiungi anche HttpGet e HttpPost prima di ogni metodo e includi il nome del controller e il nome del metodo nell'URL.

WebApiConfig =>

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

Controller =>

public class ValuesController : ApiController
{

    [HttpPost]
    public string GetCustomer([FromBody] RequestModel req)
    {
        return "Customer";
    }

    [HttpPost]
    public string GetCustomerList([FromBody] RequestModel req)
    {
        return "Customer List";
    }
}

Url =>

http://localhost:7050/api/Values/GetCustomer

http://localhost:7050/api/Values/GetCustomerList

4

Questa è la risposta per tutti coloro che sanno che tutto è corretto e ha verificato 50 volte .....

Assicurati di non guardare più volte RouteConfig.cs.

Il file che si desidera modificare è denominato WebApiConfig.cs

Inoltre, dovrebbe probabilmente apparire esattamente così:

using System.Web.Http;

namespace My.Epic.Website
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
          config.MapHttpAttributeRoutes();

          // api/Country/WithStates
          config.Routes.MapHttpRoute(
            name: "ControllerAndActionOnly",
            routeTemplate: "api/{controller}/{action}",
            defaults: new { },
            constraints: new { action = @"^[a-zA-Z]+([\s][a-zA-Z]+)*$" });

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

Mi sarei potuto risparmiare circa 3 ore.


1
Grazie, mi hai risparmiato circa 3 ore
geedubb il

3

Ho scoperto che quando ho due metodi Get, uno senza parametri e uno con un tipo complesso come parametro, ho ottenuto lo stesso errore. Ho risolto questo aggiungendo un parametro fittizio di tipo int, denominato Id, come mio primo parametro, seguito dal mio parametro di tipo complesso. Ho quindi aggiunto il parametro di tipo complesso al modello di percorso. Quanto segue ha funzionato per me.

In primo luogo ottenere:

public IEnumerable<SearchItem> Get()
{
...
}

Secondo:

public IEnumerable<SearchItem> Get(int id, [FromUri] List<string> layers)
{
...
}

WebApiConfig:

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

3

È possibile grazie all'utilizzo del controller MVC anziché del controller API Web. Controllare lo spazio dei nomi nel controller API Web dovrebbe essere come segue

using System.Net;
using System.Net.Http;
using System.Web.Http;

Se lo spazio dei nomi è il seguente, viene visualizzato l'errore sopra riportato nella chiamata del metodo del controller di API Web

using System.Web;
using System.Web.Mvc;

2

Verifica di disporre di due metodi che hanno il nome diverso e gli stessi parametri.

In tal caso, eliminare qualsiasi metodo e provare.


2

Mi sono imbattuto in questo problema mentre provavo ad aumentare i miei controller WebAPI con azioni extra.

Supponiamo che avresti

public IEnumerable<string> Get()
{
    return this.Repository.GetAll();
}

[HttpGet]
public void ReSeed()
{
    // Your custom action here
}

Esistono ora due metodi che soddisfano la richiesta di / api / controller che innesca il problema descritto da TS.

Non volevo aggiungere parametri "fittizi" alle mie azioni aggiuntive, quindi ho esaminato le azioni predefinite e ho trovato:

[ActionName("builtin")]
public IEnumerable<string> Get()
{
    return this.Repository.GetAll();
}

per il primo metodo in combinazione con l'associazione del percorso "doppia":

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { action = "builtin", id = RouteParameter.Optional },
    constraints: new { id = @"\d+" });

config.Routes.MapHttpRoute(
    name: "CustomActionApi",
    routeTemplate: "api/{controller}/{action}");

Si noti che anche se apparentemente non esiste alcun parametro "action" nel primo modello di route, è comunque possibile configurare un'azione predefinita che ci consenta di separare il routing delle chiamate "normali" WebAPI e le chiamate all'azione aggiuntiva.


2

Nel mio caso era tutto a posto

1) La configurazione Web è stata configurata correttamente 2) Il prefisso del percorso e gli attributi del percorso erano corretti

Stavo ancora ricevendo l'errore. Nel mio caso l'attributo "Route" (premendo F12) era punto su System.Web.MVc ma non su System.Web.Http che ha causato il problema.


Questa risposta mi ha aiutato molto!
tvb108108,

1

È possibile aggiungere [Route("api/[controller]/[action]")]alla classe del controller.

[Route("api/[controller]/[action]")]
[ApiController]
public class MySuperController : ControllerBase
{
 ...
}

0

So che è una vecchia domanda, ma a volte, quando si utilizzano risorse di servizio come AngularJS per connettersi a WebAPI, assicurarsi di utilizzare il percorso corretto, altrimenti si verifica questo errore.


0

Assicurati di NON decorare i tuoi metodi Controller per le azioni GET | PUT | POST | DELETE predefinite con l'attributo [HttpPost / Put / Get / Delete]. Avevo aggiunto questo attobute alla mia azione van controller Post e ha causato un 404.

Spero che questo aiuti qualcuno in quanto può essere molto frustrante e arrestare i progressi.


0

Ad esempio => TestController

        [HttpGet]
        public string TestMethod(int arg0)
        {
            return "";
        }

        [HttpGet]
        public string TestMethod2(string arg0)
        {
            return "";
        }

        [HttpGet]
        public string TestMethod3(int arg0,string arg1)
        {
            return "";
        }

Se è possibile modificare solo il file WebApiConfig.cs.

 config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/",
                defaults: null
            );

Questo è tutto :)

E risultato: inserisci qui la descrizione dell'immagine


0

Hai provato come:

[HttpGet("Summary")]
public HttpResponseMessage Summary(MyVm vm)
{
    return null;
}

[HttpGet("FullDetails")]
public HttpResponseMessage FullDetails()
{
    return null;
}

1
Questo non verrà compilato nei progetti non.NET Core, poiché l' HttpGetattributo non ha un costruttore che accetta un argomento stringa.
Hoppeduppeanut,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.