Parámetros opcionales en el enrutamiento de atributos de API web

90

Quiero manejar POST de la siguiente API-Call:

/v1/location/deviceid/appid

Los parámetros adicionales provienen del Post-Body.

Todo esto funciona bien para mí. Ahora quiero extender mi código permitiendo que "deviceid" y / o "appid" y / o BodyData sean nulos:

/v1/location/deviceid
/v1/location/appid
/v1/location/

Estas 3 URL deben responder por la misma ruta.

Mi primer enfoque (se requiere BodyData):

[Route("v1/location/{deviceid}/{appid}", Name = "AddNewLocation")]
public location_fromuser Post(string deviceid = null, string appid = null, [FromBody] location_fromuser BodyData)
{
    return repository.AddNewLocation(deviceid, appid, BodyData);
}

Esto no funciona y devuelve un error de compilación:

"Los parámetros opcionales deben estar al final"

Siguiente intento:

[Route("v1/location/{deviceid}/{appid}", Name = "AddNewLocation")]
public location_fromuser Post([FromBody] location_fromuser BodyData, string deviceid = null, string appid = null)

Ahora mi función AddNewLocation () siempre obtiene un BodyData=null- incluso si la llamada envía el Body.

Finalmente configuré los 3 parámetros opcionales:

[Route("v1/location/{deviceid}/{appid}", Name = "AddNewLocation")]
public location_fromuser Post(string deviceid = null, string appid = null, [FromBody location_fromuser BodyData = null)

No trabaje:

El parámetro opcional BodyDatano es compatible con FormatterParameterBinding.

¿Por qué quiero una solución con parámetros opcionales? Mi controlador se encarga simplemente de "agregar una nueva ubicación" a través de un POST.

Quiero enviar mis propias excepciones o mensajes de error con datos incorrectos. Incluso si a la llamada le faltan valores. En este caso, quiero poder decidir lanzar una excepción o establecer valores predeterminados por mi código.

Oliver Apel
fuente

Respuestas:

177

Para una solicitud entrante como /v1/location/1234, como puede imaginar, sería difícil para Web API averiguar automáticamente si el valor del segmento correspondiente a '1234' está relacionado con appidy no con deviceid.

Creo que debería cambiar la plantilla de su ruta para que sea similar [Route("v1/location/{deviceOrAppid?}", Name = "AddNewLocation")]y luego analizar deiveOrAppidpara averiguar el tipo de identificación.

Además, debe hacer que los segmentos en la plantilla de ruta sean opcionales; de lo contrario, los segmentos se considerarán necesarios. Tenga ?en cuenta el personaje en este caso. Por ejemplo: [Route("v1/location/{deviceOrAppid?}", Name = "AddNewLocation")]

Kiran Challa
fuente
56
?dentro de la plantilla de ruta es lo que estaba buscando. +1
Kal_Torak
4
No diría que "deviceOrAppId" es la mejor opción de diseño. Creo que la API siempre debe saber, por definición, lo que recibirá, si es posible.
Niels Brinch
14
Solo para información: cuando marcamos un parámetro como opcional en la acción uri usando un ?carácter, entonces debemos proporcionar valores predeterminados a los parámetros en la firma del método, por ejemplo, MyMethod (string name = "someDefaultValue", int? Id = null).
RBT
@RBT eres un verdadero MVP, me quedé perplejo allí por un minuto. ¡Gracias!
sm
1
Frio. Me alegro de que te haya ayudado @sm. He convertido mi comentario en una respuesta para una mejor visibilidad, ya que parece útil. Será un complemento de la publicación de Kiran.
RBT
45

Otra información: si desea usar una restricción de ruta , imagine que desea forzar que el parámetro tenga un tipo de datos int , entonces necesita usar esta sintaxis:

[Route("v1/location/**{deviceOrAppid:int?}**", Name = "AddNewLocation")]

El ? el carácter se pone siempre antes del último carácter }

Para obtener más información, consulte: Parámetros URI opcionales y valores predeterminados.

Tiago Ferreira
fuente
18

Convertir mi comentario en una respuesta para complementar la respuesta de @Kiran Chala, ya que parece útil para el público.

Cuando marcamos un parámetro como opcional en la uri de acción usando el ?carácter, entonces debemos proporcionar valores predeterminados a los parámetros en la firma del método como se muestra a continuación:

MyMethod(string name = "someDefaultValue", int? Id = null)

RBT
fuente
Estuve a punto de comentar lo mismo.
Jnr