Pojedynczy kontroler z wieloma metodami GET w ASP.NET Web API

W Web API miałem klasę o podobnej strukturze:

public class SomeController : ApiController
{
    [WebGet(UriTemplate = "{itemSource}/Items")]
    public SomeValue GetItems(CustomParam parameter) { ... }

    [WebGet(UriTemplate = "{itemSource}/Items/{parent}")]
    public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

Ponieważ mogliśmy mapować poszczególne metody, bardzo łatwo było uzyskać właściwe żądanie we właściwym miejscu. Dla podobnej klasy, która miała tylko jedną metodę GET, ale miała również parametr Object, z powodzeniem użyłem IActionValueBinder. Jednak w przypadku opisanym powyżej dostaję następujący błąd:

Multiple actions were found that match the request: 

SomeValue GetItems(CustomParam parameter) on type SomeType

SomeValue GetChildItems(CustomParam parameter, SomeObject parent) on type SomeType

Staram się podejść do tego problemu poprzez nadpisanie ExecuteAsync Metody ApiController, ale jak na razie bez powodzenia. Wszelkie porady na ten temat problem?

Edit: zapomniałem wspomnieć, że teraz próbuję przenieść ten kod na ASP.NET Web API, które ma inne podejście do routingu. Pytanie tylko, jak sprawić, by Kod działał na ASP.NET Web API?

Author: abatishchev, 2012-02-29

14 answers

Jest to najlepszy sposób, jaki znalazłem, aby wspierać dodatkowe metody GET i wspierać normalne metody odpoczynku. Dodaj następujące trasy do WebApiConfig:

routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" });
routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new {action = "Post"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Post)});

Zweryfikowałem To rozwiązanie z klasą testową poniżej. Udało mi się pomyślnie trafić każdą metodę w moim kontrolerze poniżej:

public class TestController : ApiController
{
    public string Get()
    {
        return string.Empty;
    }

    public string Get(int id)
    {
        return string.Empty;
    }

    public string GetAll()
    {
        return string.Empty;
    }

    public void Post([FromBody]string value)
    {
    }

    public void Put(int id, [FromBody]string value)
    {
    }

    public void Delete(int id)
    {
    }
}

Zweryfikowałem, że obsługuje następujące żądania:

GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1

zauważ, że jeśli dodatkowe akcje GET nie zaczynają się od 'Get', możesz dodać HttpGet atrybut metody.

 225
Author: sky-dev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-29 19:40:47

Idź od tego:

config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
            new { id = RouteParameter.Optional });

Do tego:

config.Routes.MapHttpRoute("API Default", "api/{controller}/{action}/{id}",
            new { id = RouteParameter.Optional });

Stąd możesz teraz określić, do której akcji (metody) chcesz wysłać żądanie HTTP.

Wysłanie do "http://localhost:8383/api/Command/PostCreateUser" wywołuje:

public bool PostCreateUser(CreateUserCommand command)
{
    //* ... *//
    return true;
}

I wysłanie do "http://localhost:8383/api/Command/PostMakeBooking" wywołuje:

public bool PostMakeBooking(MakeBookingCommand command)
{
    //* ... *//
    return true;
}

Próbowałem tego w aplikacji samo hostowanej usługi WEB API i działa jak urok:)

 50
Author: uggeh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-17 12:04:36

Uważam, że atrybuty są czystsze w użyciu niż ręczne dodawanie ich za pomocą kodu. Oto prosty przykład.

[RoutePrefix("api/example")]
public class ExampleController : ApiController
{
    [HttpGet]
    [Route("get1/{param1}")] //   /api/example/get1/1?param2=4
    public IHttpActionResult Get(int param1, int param2)
    {
        Object example = null;
        return Ok(example);
    }

}

Potrzebujesz tego również w swoim webapiconfig

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

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

Kilka Dobrych Linków http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api Ten lepiej wyjaśnia routing. http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

 23
Author: Kalel Wade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-25 20:21:22

Musisz zdefiniować dalsze trasy w trybie globalnym.asax.cs jak ten:

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

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);
 11
Author: Alexander Zeitler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-01 20:43:32

Nie jestem pewien, czy znalazłeś odpowiedź, ale zrobiłem to i działa

public IEnumerable<string> Get()
{
    return new string[] { "value1", "value2" };
}

// GET /api/values/5
public string Get(int id)
{
    return "value";
}

// GET /api/values/5
[HttpGet]
public string GetByFamily()
{
    return "Family value";
}

Teraz w global.asx

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

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

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
 3
Author: Pavan Josyula,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-03 09:11:31

Czy próbowałeś przełączyć się na WebInvokeAttribute i ustawić metodę na "GET"?

Wydaje mi się, że miałem podobny problem i przełączyłem się na jawne określenie, która metoda (GET/PUT/POST/DELETE) jest oczekiwana dla większości, jeśli nie wszystkich, moich metod.

public class SomeController : ApiController
{
    [WebInvoke(UriTemplate = "{itemSource}/Items"), Method="GET"]
    public SomeValue GetItems(CustomParam parameter) { ... }

    [WebInvoke(UriTemplate = "{itemSource}/Items/{parent}", Method = "GET")]
    public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

WebGet powinien obsłużyć go, ale widziałem, że ma pewne problemy z multiple Get znacznie mniej multiple Get tego samego typu.

[Edit: nic z tego nie jest poprawne z zachodem słońca WCF WebAPI i migracją do ASP.Net WebAPI na stos MVC]

 3
Author: PMontgomery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 18:30:06

Z nowszym Web Api 2 stało się łatwiejsze posiadanie wielu metod get.

Jeśli parametr przekazany do metod GET jest na tyle różny, że system trasowania atrybutów rozróżnia ich typy, jak to ma miejsce w przypadku intS I Guid s, możesz określić oczekiwany typ w atrybucie [Route...]

Na przykład -

[RoutePrefix("api/values")]
public class ValuesController : ApiController
{

    // GET api/values/7
    [Route("{id:int}")]
    public string Get(int id)
    {
       return $"You entered an int - {id}";
    }

    // GET api/values/AAC1FB7B-978B-4C39-A90D-271A031BFE5D
    [Route("{id:Guid}")]
    public string Get(Guid id)
    {
       return $"You entered a GUID - {id}";
    }
} 

Aby uzyskać więcej informacji na temat tego podejścia, zobacz tutaj http://nodogmablog.bryanhogan.net/2017/02/web-api-2-controller-with-multiple-get-methods-part-2/

Inną opcją jest nadanie metodom GET różnych tras.

    [RoutePrefix("api/values")]
    public class ValuesController : ApiController
    {
        public string Get()
        {
            return "simple get";
        }

        [Route("geta")]
        public string GetA()
        {
            return "A";
        }

        [Route("getb")]
        public string GetB()
        {
            return "B";
        }
   }

Zobacz tutaj po więcej szczegółów - http://nodogmablog.bryanhogan.net/2016/10/web-api-2-controller-with-multiple-get-methods/

 3
Author: Bryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-04 21:22:02

Próbowałem użyć trasowania atrybutów Web Api 2, aby umożliwić wiele metod Get, i włączyłem pomocne sugestie z poprzednich odpowiedzi, ale w kontrolerze miałem tylko ozdobioną "specjalną" metodę (przykład):

[Route( "special/{id}" )]
public IHttpActionResult GetSomethingSpecial( string id ) {

...Bez również umieszczania [RoutePrefix] na górze kontrolera:

[RoutePrefix("api/values")]
public class ValuesController : ApiController

Otrzymywałem błędy stwierdzające, że nie znaleziono żadnej trasy pasującej do przesłanego URI. Kiedyś miałem zarówno [trasę] dekorowanie metodą jak i [RoutePrefix] dekorowanie kontrolera jako całości, zadziałało.

 2
Author: StackOverflowUser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-05 06:51:06

W ASP.NET Core 2.0 do kontrolera można dodać atrybutRoute:

[Route("api/[controller]/[action]")]
public class SomeController : Controller
{
    public SomeValue GetItems(CustomParam parameter) { ... }

    public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}
 1
Author: maskalek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-15 12:26:37

Żaden z powyższych przykładów nie zadziałał na moje osobiste potrzeby. Poniżej znajduje się to, co zrobiłem.

 public class ContainsConstraint : IHttpRouteConstraint
{       
    public string[] array { get; set; }
    public bool match { get; set; }

    /// <summary>
    /// Check if param contains any of values listed in array.
    /// </summary>
    /// <param name="param">The param to test.</param>
    /// <param name="array">The items to compare against.</param>
    /// <param name="match">Whether we are matching or NOT matching.</param>
    public ContainsConstraint(string[] array, bool match)
    {

        this.array = array;
        this.match = match;
    }

    public bool Match(System.Net.Http.HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        if (values == null) // shouldn't ever hit this.                   
            return true;

        if (!values.ContainsKey(parameterName)) // make sure the parameter is there.
            return true;

        if (string.IsNullOrEmpty(values[parameterName].ToString())) // if the param key is empty in this case "action" add the method so it doesn't hit other methods like "GetStatus"
            values[parameterName] = request.Method.ToString();

        bool contains = array.Contains(values[parameterName]); // this is an extension but all we are doing here is check if string array contains value you can create exten like this or use LINQ or whatever u like.

        if (contains == match) // checking if we want it to match or we don't want it to match
            return true;
        return false;             

    }

Aby wykorzystać powyższe w swojej trasie użyj:

config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}", new { action = RouteParameter.Optional, id = RouteParameter.Optional}, new { action = new ContainsConstraint( new string[] { "GET", "PUT", "DELETE", "POST" }, true) });

To, co się dzieje, to rodzaj podróbek w metodzie, tak że ta trasa będzie pasować tylko do domyślnych metod GET, POST, PUT i DELETE. "True" mówi, że chcemy sprawdzić zgodność elementów w tablicy. Jeśli byłoby false, powiedziałbyś wyklucz te w strmożesz użyć tras powyżej tej domyślnej metody like:

config.Routes.MapHttpRoute("GetStatus", "{controller}/status/{status}", new { action = "GetStatus" });

W powyższym jest to zasadniczo szukanie następującego URL = > http://www.domain.com/Account/Status/Active lub coś w tym stylu.

Poza tym nie jestem pewien, czy za bardzo oszaleję. Pod koniec dnia powinno być na zasób. Ale widzę potrzebę mapowania przyjaznych adresów URL z różnych powodów. Jestem pewien, że wraz z rozwojem Web Api pojawi się jakiś przepis. Jeśli czas zbuduję bardziej trwałe rozwiązanie i post.
 0
Author: origin1tech,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-22 21:51:41

Nie udało się uruchomić żadnego z powyższych rozwiązań routingu -- część składni wydaje się być zmieniona i wciąż jestem nowy w MVC -- w szczypcie, chociaż ułożyłem ten naprawdę okropny (i prosty) hack, który na razie mnie dopadnie -- Uwaga, zastępuje to metodę" public Myobject GetMyObjects (long id)" -- zmieniamy Typ " id " na string i zmieniamy Typ powrotu na obiekt.

// GET api/MyObjects/5
// GET api/MyObjects/function
public object GetMyObjects(string id)
{
    id = (id ?? "").Trim();

    // Check to see if "id" is equal to a "command" we support
    // and return alternate data.

    if (string.Equals(id, "count", StringComparison.OrdinalIgnoreCase))
    {
        return db.MyObjects.LongCount();
    }

    // We now return you back to your regularly scheduled
    // web service handler (more or less)

    var myObject = db.MyObjects.Find(long.Parse(id));
    if (myObject == null)
    {
        throw new HttpResponseException
        (
            Request.CreateResponse(HttpStatusCode.NotFound)
        );
    }

    return myObject;
}
 0
Author: BrainSlugs83,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-18 02:15:13

Jeśli masz wiele akcji w tym samym pliku, przekaż ten sam argument, np. Id do wszystkich akcji. Dzieje się tak dlatego, że action może identyfikować tylko Id, więc zamiast podawać jakąkolwiek nazwę argumentowi deklaruje tylko Id w ten sposób.


[httpget]
[ActionName("firstAction")] firstAction(string Id)
{.....
.....
}
[httpget]
[ActionName("secondAction")] secondAction(Int Id)
{.....
.....
}
//Now go to webroute.config file under App-start folder and add following
routes.MapHttpRoute(
name: "firstAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);

routes.MapHttpRoute(
name: "secondAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
 0
Author: Uttam Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-12 16:30:37

Simple Alternative

Wystarczy użyć ciągu zapytania.

Routing

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

Controller

public class TestController : ApiController
{
    public IEnumerable<SomeViewModel> Get()
    {
    }

    public SomeViewModel GetById(int objectId)
    {
    }
}

Requests

GET /Test
GET /Test?objectId=1

Uwaga

Należy pamiętać, że łańcuch zapytania param nie powinien być " id " lub jakikolwiek parametr jest w skonfigurowanej trasie.

 0
Author: seth flowers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-30 15:37:38

Zmodyfikuj WebApiConfig i dodaj na końcu kolejne trasy.MapHttpRoute like this:

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

Następnie utwórz kontroler w ten sposób:

public class ServiceController : ApiController
{
        [HttpGet]
        public string Get(int id)
        {
            return "object of id id";
        }
        [HttpGet]
        public IQueryable<DropDownModel> DropDowEmpresa()
        {
            return db.Empresa.Where(x => x.Activo == true).Select(y =>
                  new DropDownModel
                  {
                      Id = y.Id,
                      Value = y.Nombre,
                  });
        }

        [HttpGet]
        public IQueryable<DropDownModel> DropDowTipoContacto()
        {
            return db.TipoContacto.Select(y =>
                  new DropDownModel
                  {
                      Id = y.Id,
                      Value = y.Nombre,
                  });
        }

        [HttpGet]
        public string FindProductsByName()
        {
            return "FindProductsByName";
        }
}
Tak to rozwiązałem. Mam nadzieję, że to komuś pomoże.
 -1
Author: Eduardo Mercado,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-06 18:19:50