Caractère de point '.' dans MVC Web API 2 pour une demande telle que api / people / STAFF.45287

106

L'URL que j'essaie de laisser travailler est celle du style: http://somedomain.com/api/people/staff.33311 (tout comme les sites comme LAST.FM autorisent toutes sortes de signes dans leurs URL RESTFul & WebPage , par exemple " http://www.last.fm/artist/psy'aviah " est une URL valide pour LAST.FM).

Ce qui fonctionne, ce sont les scénarios suivants: - http://somedomain.com/api/people/ - qui renvoie toutes les personnes - http://somedomain.com/api/people/staff33311 - fonctionnerait également, mais ce n'est pas ce que je ' m après je voudrais que l'URL accepte un "point", comme l'exemple ci-dessous - http://somedomain.com/api/people/staff.33311 - mais cela me donne un

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

J'ai mis en place les choses suivantes:

  1. Le contrôleur "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. Le 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 }
        );
    }

J'ai déjà essayé de suivre tous les conseils de ce blog http://www.hanselman.com/blog/ExperimentsInWackinessAllowingPercentsAnglebracketsAndOtherNaughtyThingsInTheASPNETIISRequestURL.aspx mais ça ne fonctionnera toujours pas .. Je pense aussi que c'est assez fastidieux et je me demande s'il n'y en a pas d'autre, manière meilleure et plus sûre.

Nous avons nos identifiants en interne comme ça, donc nous allons devoir trouver une solution pour adapter le point d'une manière ou d'une autre, de préférence dans le style "." mais je suis ouvert à d'autres suggestions d'urls si besoin est ...

Yves Schelpe
la source
10
Pas une réponse, mais pourquoi vous obtenez un 404 pour somedomain.com/api/people/staff.33311 - par défaut, IIS regarde cette URL et voit le fichier. en tant qu'extension de fichier et appelle le gestionnaire de fichier statique, en contournant votre API MVC. La réponse que vous avez acceptée (exécuter tous les modules gérés pour toutes les requêtes) fonctionne car vous forcez chaque requête à IIS à passer par le pipeline ASP.NET (par conséquent, vos contrôleurs)
Henry C
Post étroitement lié ici .
RBT

Réponses:

104

Le paramètre suivant dans votre web.configfichier devrait résoudre votre problème:

<configuration>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true" />
Kiran Challa
la source
4
Ça a fait l'affaire. Cependant, y a-t-il des vulnérabilités en définissant cette option? Pourquoi n'est-ce pas le comportement standard?
Yves Schelpe
2
Ok, pour l'intérêt de tous: je vois que ma question ci-dessus a été répondue ici sur la réponse acceptée au moment de la rédaction (réponse de Kapil Khandelwal): stackoverflow.com/questions/11048863/…
Yves Schelpe
2
Oui, cela a fonctionné, en bus d'autres, je voudrais savoir quel module particulier est nécessaire pour que cette fonctionnalité fonctionne?
Greg Z.
3
Quand j'essayais cela, cela ne fonctionnerait que si je mettais un «/» à la fin du chemin. Des suggestions pourquoi c'est? Sur une note latérale, la réponse ci-dessous qui ajoute spécifiquement le 'UrlRoutingModule' au lieu d'exécuter tous les modules, a également fonctionné pour moi, bien que toujours avec le problème qu'il nécessite un '/' à la fin pour fonctionner.
Nikolaj Dam Larsen
10
stackoverflow.com/a/12151501/167018 vaut la peine d'être examiné si vous êtes préoccupé (et à juste titre) par l'impact sur les performances de l'activation de runAllManagedModulesForAllRequests.
Henry C
140

Suffixez l'URL avec une barre oblique, par exemple http://somedomain.com/api/people/staff.33311/au lieu de http://somedomain.com/api/people/staff.33311.

Danny Varod
la source
3
@AgustinMeriles ma réponse pourrait être considérée plus comme une solution de contournement qu'une réponse réelle, cela dépend de votre interprétation de la question.
Danny Varod
5
Cela ne fonctionne pas lorsque le point est à la fin de la section URL comme dans / people / member.
eYe
2
Non, et si je ne veux pas de barre oblique à la fin?! Cela ne devrait pas être obligatoire.
Josh M.
1
@JoshM. C'est une solution de contournement, je n'ai pas écrit ASP.NET. De plus, la barre oblique n'affecte pas la demande. Il, cependant, aider à rendre vos intentions plus claires en google.com/my query goes here/contre google.com/subDomain my query goes here.
Danny Varod
1
Oui, c'est le mieux que vous ne devriez pas avoir à modifier le web.config.
Timothy Gonzalez
35

J'ai constaté que l' ajout de ce qui suit avant la norme ExtensionlessUrlHandlerrésout le problème pour moi:

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

Je ne pense pas que le nom compte vraiment autant, sauf qu'il aide probablement si votre IDE (Visual Studio dans mon cas) gère la configuration de votre site.

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

BrianS
la source
Je l'ai ajouté «après» la ligne standard et cela a également bien fonctionné.
Jalal El-Shaer
Cela devrait être la réponse acceptée. Ne nécessite pas / à la fin de l'uri
daudihus
2
Merci pour le BEFORE, car cela ne fonctionnait pas après!
Mese
Je crois que les modules fonctionnent dans l'ordre de déclaration, alors mettez toujours les plus spécifiques (ou les plus importants) avant les plus génériques.
BrianS
24

Je ne sais pas vraiment ce que je fais, mais après avoir joué un peu avec la réponse précédente, j'ai trouvé une autre solution, peut-être plus appropriée:

<system.webServer>
<modules>
    <remove name="UrlRoutingModule-4.0" />
    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
</modules>
</system.webServer>
Greg Z.
la source
Merci, je ne sais pas non plus ce qu'il fait mais ça a l'air mieux que l'autre solution.
Thomas
1
semble déplacer le module de routage d'url (ce qui attrape le fait que la requête inclut une extension puis essaie de la gérer comme un fichier) à la fin de la liste des modules, ce qui est suffisant pour le placer après le module gérant la requête api . IMHO, c'est la meilleure solution de contournement disponible parmi celles que j'ai vues sur SO, au moins ATTOW
James Manning
1
Ce n'est pas le déplacement à la fin de la liste, c'est la suppression de la condition préalable par défaut que ce module ne fonctionne que pour les gestionnaires gérés. La configuration par défaut utilise ce format:<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler" />
theta-fish
Merci - cela expliquerait le comportement.
Greg Z.23
8

J'ai trouvé que je devais faire plus que simplement définir l' runAllManagedModulesForAllRequestsattribut true. Je devais également m'assurer que le gestionnaire d'URL sans extension était configuré pour regarder tous les chemins. De plus, vous pouvez ajouter un paramètre de configuration supplémentaire qui vous aidera dans certains cas. Voici mon Web.config de travail:

<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>

Note, en particulier, que le ExtensionlessUrlHandler-Integrated-4.0a son pathensemble d'attributs à *par opposition à *.(par exemple).

Josh M.
la source
Je viens de me faire mordre path="*.". Juste curieux, quelle est la raison pour laquelle les gens ont choisi cela path="*."?
JustinP8
En fait, je l'ai changé et j'ai path="*"eu un problème parce que nous hébergeons un site de documentation côte à côte avec notre WebAPI et que ce site avait des problèmes avec .jpg, .png et d'autres fichiers avec des extensions.
JustinP8
@ JustinP8 Avez-vous également mis <modules runAllManagedModulesForAllRequests="true" />? Cela devrait permettre à .NET de gérer ces fichiers statiques.
Josh M.
1
Oui. J'ai fini par faire ça. Je n'aime pas vraiment ça parce que maintenant, tous les fichiers statiques passent par le pipeline .NET. Heureusement, comme il s'agit d'un service webAPI, seuls les éléments Swagger et Swashbuckle pour la documentation / le site d'assistance de l'API sont affectés.
JustinP8
J'ai trouvé que cela cassait toutes les demandes de fichiers statiques. erreur 500. J'avais runAllManaged ... mis à true.
Sam
2

Je suis resté coincé dans cette situation, mais l'ajout / à la fin de l'URL ne me paraissait pas clair.

alors ajoutez simplement ci-dessous dans la balise web.config handlers et vous serez prêt à partir.

<add name="Nancy" path="api" verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" allowPathInfo="true" />
Khawaja Asim
la source
Pouvez-vous expliquer ce qu'est Nancy et si c'est une bibliothèque à inclure dans le projet? Merci!
bleuté
1

J'ai trouvé que les deux méthodes fonctionnent pour moi: soit en définissant runAllManagedModulesForAllRequests sur true, soit en ajoutant ExtentionlessUrlHandler comme suit. Enfin, je choisis d'ajouter extensionUrLHandler car runAllManagedModulesForAllRequests a un impact sur les performances du site.

<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
la source
1

J'utiliserais ceci dans le fichier Web.config:

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

avant "ExtensionlessUrlHandler" standard.

Par exemple, dans mon cas, je l'ai mis ici:

<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>

Vous forcez donc la gestion des URL d'un tel modèle, au lieu de la gestion standard sous forme de fichiers dans l'arborescence des répertoires d'applications.

bleuâtre
la source
0

J'ai été confronté au même problème et aux circonstances dans lesquelles je n'étais pas censé jouer avec IIS et les paramètres liés à la configuration du site Web. J'ai donc dû le faire fonctionner en apportant des modifications au niveau du code uniquement.

Le point simple est que le cas le plus courant où vous finirez par avoir un caractère point dans l'URL est lorsque vous obtenez une entrée de l'utilisateur et que vous la transmettez sous forme de chaîne de requête ou de fragment d'URL pour passer un argument aux paramètres de la méthode d'action de votre contrôleur.

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

Jetez un œil à l'URL ci-dessous où l'utilisateur entre son identifiant pour obtenir ses informations personnelles:

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

Puisque vous devez simplement récupérer des données du serveur, nous utilisons le GETverbe http . En utilisantGET appels, tous les paramètres d'entrée ne peuvent être transmis que dans les fragments d'URL.

Donc, pour résoudre mon problème, j'ai changé le verbe http de mon action en POST. Le POSTverbe HTTP a la possibilité de transmettre également toute entrée utilisateur ou non utilisateur dans le corps. J'ai donc créé une donnée JSON et l'ai passée dans le corps de la POSTrequête http :

{
  "userid" : "foo.bar"
}

Modifiez la définition de votre méthode comme ci-dessous:

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

Remarque : Plus d'informations sur quand utiliser GETverbe et quand utiliser POSTverbe ici .

RBT
la source
Ce n'est pas une solution appropriée si vous créez une API REST.
Mustafa Ozturk