Carácter de punto '.' en MVC Web API 2 para solicitudes como api / people / STAFF.45287

106

La URL que intento que funcione es una del estilo de: http://somedomain.com/api/people/staff.33311 (al igual que los sitios como LAST.FM permiten todo tipo de signos en sus URL RESTFul y WebPage , por ejemplo, " http://www.last.fm/artist/psy'aviah " es una URL válida para LAST.FM).

Lo que funciona son los siguientes escenarios: - http://somedomain.com/api/people/ - que devuelve todas las personas - http://somedomain.com/api/people/staff33311 - funcionaría también, pero no es lo que yo ' m después de que quisiera que la URL aceptara un "punto", como en el ejemplo siguiente, http://somedomain.com/api/people/staff.33311 , pero esto me da una

HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

He configurado las siguientes cosas:

  1. El controlador "PeopleController"

    public IEnumerable<Person> GetAllPeople()
    {
        return _people;
    }
    
    public IHttpActionResult GetPerson(string id)
    {
        var person = _people.FirstOrDefault(p => p.Id.ToLower().Equals(id.ToLower()));
        if (person == null)
            return NotFound();
    
        return Ok(person);
    }    
  2. WebApiConfig.cs

    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
    
        // Web API routes
        config.MapHttpAttributeRoutes();
    
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }

Ya intenté seguir todos los consejos de esta publicación de blog http://www.hanselman.com/blog/ExperimentsInWackinessAllowingPercentsAnglebracketsAndOtherNaughtyThingsInTheASPNETIISRequestURL.aspx pero aún así no funcionará. También creo que es bastante tedioso y me pregunto si no hay otro, forma mejor y más segura.

Tenemos nuestras identificaciones internas así, así que tendremos que encontrar una solución para ajustar el punto de una forma u otra, preferiblemente en el estilo de "." pero estoy abierto a sugerencias alternativas de URL si es necesario ...

Yves Schelpe
fuente
10
No es una respuesta, pero en cuanto a por qué está obteniendo un 404 para somedomain.com/api/people/staff.33311 : de forma predeterminada, IIS mira esta URL y ve el. como una extensión de archivo e invoca el controlador de archivos estáticos, sin pasar por la API de MVC. La respuesta que aceptó (ejecutar todos los módulos administrados para todas las solicitudes) funciona porque obliga a que todas las solicitudes a IIS pasen por la canalización ASP.NET (por lo tanto, sus controladores)
Henry C
Publicación estrechamente relacionada aquí .
RBT

Respuestas:

104

La siguiente configuración en su web.configarchivo debería solucionar su problema:

<configuration>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true" />
Kiran Challa
fuente
4
Hizo el truco. Sin embargo, ¿existen vulnerabilidades al configurar esta opción? ¿Por qué no es este el comportamiento estándar?
Yves Schelpe
2
Ok, para el interés de todos: Veo que mi pregunta anterior se responde aquí en la respuesta aceptada en el momento de escribir este artículo (respuesta de Kapil Khandelwal): stackoverflow.com/questions/11048863/…
Yves Schelpe
2
Sí, funcionó, bus de otros, me gustaría saber qué módulo en particular se requiere para que funcione esta funcionalidad.
Greg Z.17 de
3
Cuando probé esto, solo funcionaría si ponía una '/' al final de la ruta. ¿Alguna sugerencia de por qué es esto? En una nota al margen, la respuesta a continuación que agrega específicamente el 'UrlRoutingModule' en lugar de ejecutar todos los módulos, también funcionó para mí, aunque todavía con el problema de que requiere un '/' al final para funcionar.
Nikolaj Dam Larsen
10
Stackoverflow.com/a/12151501/167018 vale la pena mirar si le preocupa (y con razón) el impacto en el rendimiento de habilitar runAllManagedModulesForAllRequests.
Henry C
140

Agregue un sufijo a la URL, por ejemplo, en http://somedomain.com/api/people/staff.33311/lugar de http://somedomain.com/api/people/staff.33311.

Danny Varod
fuente
3
@AgustinMeriles mi respuesta podría considerarse más una solución alternativa que una respuesta real, depende de su interpretación de la pregunta.
Danny Varod
5
Sin embargo, eso no funciona cuando el punto está al final de la sección de URL como en / people / member.
eYe
2
No, ¿y si no quiero una barra al final? Debería no ser necesario.
Josh M.
1
@JoshM. Esta es una solución alternativa, no escribí ASP.NET. Además, la barra no afecta la solicitud. Lo hace, sin embargo, ayuda a que sus intenciones más claras como en google.com/my query goes here/contra google.com/subDomain my query goes here.
Danny Varod
1
Sí, esto es lo mejor que no debería tener para modificar web.config.
Timothy Gonzalez
35

Descubrí que agregar lo siguiente antes del estándar me ExtensionlessUrlHandlerresuelve el problema:

<add name="ExtensionlessUrlHandler-Integrated-4.0-ForApi"
     path="api/*"
     verb="*"
     type="System.Web.Handlers.TransferRequestHandler"
     preCondition="integratedMode,runtimeVersionv4.0" />

No creo que el nombre realmente importe demasiado, excepto que probablemente ayude si su IDE (Visual Studio en mi caso) está administrando la configuración de su sitio.

H / T a https://stackoverflow.com/a/15802305/264628

BrianS
fuente
Lo agregué 'después' de la línea estándar y también funcionó bien.
Jalal El-Shaer
Esta debería ser la respuesta aceptada. No requiere / al final de uri
daudihus
2
Gracias por el ANTES, ¡porque no funcionó después!
Mese
Creo que los módulos se ejecutan en orden de declaración, así que siempre ponga los más específicos (o más importantes) antes que los más genéricos.
BrianS
24

No sé qué estoy haciendo realmente, pero después de jugar un poco con la respuesta anterior, se me ocurrió otra solución, quizás más apropiada:

<system.webServer>
<modules>
    <remove name="UrlRoutingModule-4.0" />
    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
</modules>
</system.webServer>
Greg Z.
fuente
Gracias, tampoco sé qué está haciendo, pero se ve mejor que la otra solución.
Thomas
1
parece mover el módulo de enrutamiento de url (que es lo que detecta que la solicitud incluye una extensión y luego intenta manejarla como un archivo) al final de la lista de módulos, lo cual es suficiente para ponerlo después del módulo que maneja la solicitud de la API . En mi humilde opinión, esta es la mejor solución disponible de las que he visto en SO, al menos ATTOW
James Manning
1
No es la reubicación al final de la lista, es la eliminación de la condición previa predeterminada de que este módulo solo se ejecuta para controladores administrados. La configuración predeterminada usa este formato:<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler" />
theta-fish
Gracias, eso explicaría el comportamiento.
Greg Z.23 de
8

Descubrí que necesitaba hacer algo más que establecer el runAllManagedModulesForAllRequestsatributo en true. También tuve que asegurarme de que el controlador de URL sin extensión estuviera configurado para buscar en todas las rutas. Además, hay una opción de configuración adicional que puede agregar y que ayudará en algunos casos. Aquí está mi Web.config de trabajo:

<system.web>
    <httpRuntime relaxedUrlToFileSystemMapping="true" />
</system.web>
<system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
    <handlers>
        <remove name="WebDAV" />
        <remove name="OPTIONSVerbHandler" />
        <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
        <add name="ExtensionlessUrlHandler-Integrated-4.0"  path="*" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
</system.webServer>

Tenga en cuenta, específicamente, que ExtensionlessUrlHandler-Integrated-4.0tiene su pathatributo establecido *como opuesto a *.(por ejemplo).

Josh M.
fuente
Me acaba de morder el path="*.". Solo por curiosidad, ¿cuál es la razón por la que la gente lo establece path="*."?
JustinP8
En realidad, lo cambié a path="*"y tuve un problema porque estamos alojando un sitio de documentación al lado de nuestra WebAPI y ese sitio tenía problemas con .jpg, .png y otros archivos con extensiones.
JustinP8
@ JustinP8 ¿También estableciste <modules runAllManagedModulesForAllRequests="true" />? Eso debería hacer que .NET maneje esos archivos estáticos.
Josh M.
1
Si. Terminé haciendo eso. Sin embargo, realmente no me gusta eso porque ahora todos los archivos estáticos pasan por la tubería .NET. Afortunadamente, como este es un servicio webAPI, solo se ven afectadas las cosas Swagger y Swashbuckle para los documentos API / sitio de ayuda.
JustinP8
Encontré que esto rompió todas las solicitudes de archivos estáticos. error 500. Había runAllManaged ... establecido en verdadero.
Sam
2

Me quedé atascado en esta situación, pero agregar / al final de la URL no se veía limpio para mí.

así que simplemente agregue a continuación en la etiqueta web.config handlers y estará listo para comenzar.

<add name="Nancy" path="api" verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" allowPathInfo="true" />
Khawaja Asim
fuente
¿Puedes explicar qué es Nancy y si se trata de una biblioteca que se incluirá en el proyecto? ¡Gracias!
azulado
1

Descubrí que ambas formas funcionan para mí: ya sea configurando runAllManagedModulesForAllRequests en verdadero o agregue ExtentionlessUrlHandler de la siguiente manera. Finalmente, elijo agregar extensionUrLHandler ya que runAllManagedModulesForAllRequests tiene un impacto en el rendimiento del sitio.

<handlers>
  <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  <remove name="OPTIONSVerbHandler" />
  <remove name="TRACEVerbHandler" />
  <remove name="WebDAV" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*" verb="*" 
       type="System.Web.Handlers.TransferRequestHandler" 
       preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
Xuemei
fuente
1

Usaría esto en el archivo Web.config:

<add name="ManagedSpecialNames" path="api/people/*" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />

antes del estándar "ExtensionlessUrlHandler".

Por ejemplo en mi caso lo pongo aquí:

<handlers>
  <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  <remove name="OPTIONSVerbHandler" />
  <remove name="TRACEVerbHandler" />
  <add name="ManagedFiles" path="api/people/*" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>

Por lo tanto, obliga a que las URL de dicho patrón sean administradas por usted, en lugar de la administración estándar como archivos en el árbol de directorios de la aplicación.

azulado
fuente
0

Enfrenté el mismo problema y las circunstancias en las que estaba en las que se suponía que no debía jugar con IIS y configuraciones relacionadas con la configuración del sitio web. Así que tuve que hacer que funcionara haciendo cambios solo a nivel de código.

El punto simple es que el caso más común en el que terminaría teniendo un carácter de punto en la URL es cuando obtiene alguna entrada del usuario y la pasa como una cadena de consulta o un fragmento de URL para pasar algún argumento a los parámetros en el método de acción de su controlador.

public class GetuserdetailsbyuseridController : ApiController
{
     string getuserdetailsbyuserid(string userId)
     {
        //some code to get user details
     }
}

Eche un vistazo a la siguiente URL donde el usuario ingresa su identificación de usuario para obtener sus datos personales:

http://mywebsite:8080/getuserdetailsbyuserid/foo.bar

Dado que simplemente tiene que buscar algunos datos del servidor, usamos el GETverbo http . Durante el uso de GETllamadas, los parámetros de entrada solo se pueden pasar en los fragmentos de URL.

Entonces, para resolver mi problema, cambié el verbo http de mi acción a POST. Http POSTverb tiene la facilidad de pasar cualquier entrada de usuario o no usuario en el cuerpo también. Así que creé un dato JSON y lo pasé al cuerpo de la POSTsolicitud http :

{
  "userid" : "foo.bar"
}

Cambie la definición de su método de la siguiente manera:

public class GetuserdetailsbyuseridController : ApiController
{
     [Post]
     string getuserdetailsbyuserid([FromBody] string userId)
     {
        //some code to get user details
     }
}

Nota : Más información sobre cuándo usar el GETverbo y cuándo usar el POSTverbo aquí .

RBT
fuente
No es una solución adecuada si está creando una API REST.
Mustafa Ozturk