Controlador único con múltiples métodos GET en ASP.NET API WEB


En Web API tenía una clase de estructura similar:

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

Dado que podíamos mapear métodos individuales, era muy simple obtener la solicitud correcta en el lugar correcto. Para una clase similar que solo tenía un único método GET pero también tenía un parámetro Object, utilicé con éxito IActionValueBinder. Sin embargo, en el caso descrito anteriormente obtengo el siguiente error:

Multiple actions were found that match the request: 

SomeValue GetItems(CustomParam parameter) on type SomeType

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

Estoy tratando de abordar este problema anulando el método ExecuteAsync de ApiController pero sin suerte hasta ahora. Cualquier consejo sobre esto problema?

Editar: Olvidé mencionar que ahora estoy tratando de mover este código ASP.NET Web API que tiene un enfoque diferente para el enrutamiento. La pregunta es, ¿cómo hago que el código funcione en ASP.NET ¿API WEB?

Author: abatishchev, 2012-02-29

14 answers

Esta es la mejor manera que he encontrado para apoyar los métodos extra GET y apoyar los métodos normales de DESCANSO también. Agregue las siguientes rutas a su 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)});

He verificado esta solución con la clase de prueba a continuación. Pude golpear con éxito cada método en mi controlador a continuación:

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

He comprobado que soporta las siguientes peticiones:

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

Tenga en cuenta Que si sus acciones GET adicionales no comienzan con 'Get', es posible que desee agregar un HttpGet atributo al método.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-29 19:40:47

Ir de esto:

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

A esto:

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

Por lo tanto, ahora puede especificar a qué acción (método) desea enviar su solicitud HTTP.

Enviando a "http://localhost:8383/api/Command/PostCreateUser" invoca:

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

Y posteando a "http://localhost:8383/api/Command/PostMakeBooking" invoca:

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

Probé esto en una aplicación de servicio API WEB auto alojada y funciona como un encanto:)

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-17 12:04:36

Encuentro atributos más limpios de usar que agregarlos manualmente a través de código. He aquí un ejemplo sencillo.

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

}

También necesitas esto en tu 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 }
);

Algunos Buenos Enlaces http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api Este explica mejor el enrutamiento. 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-25 20:21:22

Necesita definir rutas adicionales en global.asax.cs como este:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-01 20:43:32

No estoy seguro de si has encontrado la respuesta, pero lo hice y funciona

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

Ahora en 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-03 09:11:31

¿Ha intentado cambiar a WebInvokeAttribute y establecer el Método en "GET"?

Creo que tuve un problema similar y cambié a decir explícitamente qué Método (GET/PUT/POST/DELETE) se espera en la mayoría, si no en todos, mis métodos.

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

El WebGet debería manejarlo, pero he visto que tiene algunos problemas con multiple Get mucho menos multiple Get del mismo tipo de retorno.

[Editar: nada de esto es válido con la puesta de sol de WCF WebAPI y la migración a ASP.Net WebAPI en la pila 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-17 18:30:06

Con la nueva Web Api 2 se ha vuelto más fácil tener múltiples métodos get.

Si el parámetro pasado a los métodos GET es lo suficientemente diferente para que el sistema de enrutamiento de atributos distinga sus tipos como es el caso con int s y Guid s, puede especificar el tipo esperado en el atributo [Route...]

Por ejemplo -

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

Para obtener más detalles sobre este enfoque, consulte aquí http://nodogmablog.bryanhogan.net/2017/02/web-api-2-controller-with-multiple-get-methods-part-2/

Otra opción es dar a los métodos GET rutas diferentes.

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

Vea aquí para más detalles - 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-04 21:22:02

Estaba tratando de usar el enrutamiento de atributos de Web Api 2 para permitir múltiples métodos Get, y había incorporado las sugerencias útiles de respuestas anteriores, pero en el Controlador solo había decorado el método "especial" (ejemplo):

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

...sin también colocar un [RoutePrefix] en la parte superior del Controlador:

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

Estaba recibiendo errores que indican que no se encontró ninguna ruta que coincida con el URI enviado. Una vez tuve tanto la [Ruta] decorar el método, así como [RoutePrefix] decorar el Controlador como un todo, funcionó.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-05 06:51:06

En ASP.NET Core 2.0 puede agregar el atributo Route al controlador:

[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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-15 12:26:37

Ninguno de los ejemplos anteriores funcionó para mis necesidades personales. Lo siguiente es lo que terminé haciendo.

 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;             

    }

Para usar lo anterior en su ruta use:

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

Lo que sucede es el tipo de restricción de falsificaciones en el método para que esta ruta solo coincida con los métodos predeterminados GET, POST, PUT y DELETE. El "verdadero" allí dice que queremos comprobar si hay una coincidencia de los elementos en la matriz. Si fuera false estaría diciendo excluir aquellos en el strYou puede usar rutas por encima de este método predeterminado como:

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

En lo anterior, esencialmente está buscando la siguiente URL => http://www.domain.com/Account/Status/Active o algo así.

Más allá de lo anterior no estoy seguro de que me volvería demasiado loco. Al final del día debe ser por recurso. Pero veo la necesidad de mapear urls amigables por varias razones. Me siento bastante seguro de que a medida que evolucione la Api web habrá algún tipo de provisión. Si el tiempo voy a construir una solución más permanente y 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-22 21:51:41

No pude hacer que ninguna de las soluciones de enrutamiento anteriores funcionaran some parte de la sintaxis parece haber cambiado y todavía soy nuevo en MVC though en un apuro, aunque armé este truco realmente horrible (y simple) que me ayudará por ahora note nota, esto reemplaza el método "public myObject GetMyObjects(long id)" change cambiamos el tipo de "id"a una cadena, y cambiamos el tipo de retorno a object.

// 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-18 02:15:13

Si tiene varias Acciones dentro del mismo archivo, pase el mismo argumento, por ejemplo, Id a todas las acciones. Esto se debe a que la acción solo puede identificar Id, por lo que en lugar de dar cualquier nombre al argumento solo declare Id como este.


[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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-12 16:30:37

Alternativa simple

Simplemente use una cadena de consulta.

Enrutamiento

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

Controlador

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

    public SomeViewModel GetById(int objectId)
    {
    }
}

Solicitudes

GET /Test
GET /Test?objectId=1

Nota

Tenga en cuenta que el parámetro de cadena de consulta no debe ser "id" o cualquiera que sea el parámetro en la ruta configurada.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-30 15:37:38

Modifica el WebApiConfig y añade al final otras rutas.MapHttpRoute como este:

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

Luego crea un controlador como este:

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

Así es como lo resolví. Espero que ayude a alguien.

 -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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-06 18:19:50