Carattere punto "." in MVC Web API 2 per richieste come api / people / STAFF.45287


106

L'URL che sto cercando di far funzionare è nello stile di: http://somedomain.com/api/people/staff.33311 (proprio come i siti come LAST.FM consentono tutti i tipi di segni nei loro URL RESTFul e WebPage , ad esempio " http://www.last.fm/artist/psy'aviah " è un URL valido per LAST.FM).

Ciò che funziona sono i seguenti scenari: - http://somedomain.com/api/people/ - che restituisce tutte le persone - http://somedomain.com/api/people/staff33311 - funzionerebbe anche, ma non è quello che io ' m dopo vorrei che l'URL accetti un "punto", come nell'esempio seguente - http://somedomain.com/api/people/staff.33311 - ma questo mi dà un

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

Ho impostato le seguenti cose:

  1. Il controller "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. Il 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 }
        );
    }

Ho già provato a seguire tutti i suggerimenti di questo post del blog http://www.hanselman.com/blog/ExperimentsInWackinessAllowingPercentsAnglebracketsAndOtherNaughtyThingsInTheASPNETIISRequestURL.aspx ma continua a non funzionare .. Penso anche che sia abbastanza noioso e mi chiedo se non ce ne sia un altro, modo migliore e più sicuro.

Abbiamo i nostri ID internamente in questo modo, quindi dovremo trovare una soluzione per adattare il punto in un modo o nell'altro, preferibilmente nello stile di "." ma sono aperto a suggerimenti alternativi per gli URL, se necessario ...


10
Non una risposta, ma per quanto riguarda il motivo per cui stai ottenendo un 404 per somedomain.com/api/people/staff.33311 : per impostazione predefinita, IIS guarda questo URL e vede il file. come estensione di file e richiama il gestore di file statico, bypassando l'API MVC. La risposta che hai accettato (eseguendo tutti i moduli gestiti per tutte le richieste) funziona perché costringi ogni richiesta a IIS a passare attraverso la pipeline ASP.NET (quindi, i tuoi controller)
Henry C

Post strettamente correlato qui .
RBT

Risposte:


104

Seguendo l'impostazione nel tuo web.config file dovrebbe risolvere il problema:

<configuration>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true" />

4
Ha funzionato. Tuttavia, ci sono vulnerabilità impostando questa opzione? Perché non è questo il comportamento standard?
Yves Schelpe

2
Ok, per interesse di nessuno: vedo la mia domanda di cui sopra ricevere risposta qui la risposta accettata al momento della scrittura (risposta da Kapil Khandelwal): stackoverflow.com/questions/11048863/...
Yves Schelpe

2
Sì, ha funzionato, bus altri, vorrei sapere quale particolare modulo è necessario per far funzionare questa funzionalità?
Greg Z.

3
Quando l'ho provato, funzionava solo se inserivo un "/" alla fine del percorso. Qualche suggerimento sul perché questo è? In una nota a margine, la risposta di seguito che aggiunge specificamente "UrlRoutingModule" invece di eseguire tutti i moduli, ha funzionato anche per me, sebbene ancora con il problema che richiede un "/" alla fine per funzionare.
Nikolaj Dam Larsen

10
stackoverflow.com/a/12151501/167018 merita di essere esaminato se sei preoccupato (e giustamente) dell'impatto sulle prestazioni dell'abilitazione di runAllManagedModulesForAllRequests.
Henry C

140

Aggiungi all'URL una barra, ad esempio http://somedomain.com/api/people/staff.33311/invece di http://somedomain.com/api/people/staff.33311.


3
@AgustinMeriles la mia risposta potrebbe essere considerata più una soluzione alternativa che una risposta effettiva, dipende dalla tua interpretazione della domanda.
Danny Varod

5
Ciò non funziona però quando il punto si trova alla fine della sezione URL come in / people / member.
eYe

2
No, e se non volessi un taglio alla fine ?! Dovrebbe non essere necessario.
Josh M.

1
@JoshM. Questa è una soluzione alternativa, non ho scritto ASP.NET. Inoltre, la barra non influisce sulla richiesta. Essa, tuttavia, contribuire a rendere le vostre intenzioni più chiaro come in google.com/my query goes here/contro google.com/subDomain my query goes here.
Danny Varod

1
Sì, questo è il meglio che non dovresti avere per modificare il file web.config.
Timothy Gonzalez

35

Ho scoperto che l' aggiunta di quanto segue prima dello standard ExtensionlessUrlHandlerrisolve il problema per me:

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

Non credo che il nome sia davvero così importante tranne che probabilmente aiuta se il tuo IDE (Visual Studio nel mio caso) gestisce la configurazione del tuo sito.

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


L'ho aggiunto "dopo" la linea standard e ha anche funzionato bene.
Jalal El-Shaer

Questa dovrebbe essere la risposta accettata. Non richiede / alla fine di uri
daudihus

2
Grazie per il PRIMA, perché non funzionava dopo!
Mese

Credo che i moduli vengano eseguiti in ordine di dichiarazione, quindi metti sempre quelli più specifici (o più importanti) prima di quelli più generici.
BrianS,

24

Non so cosa sto facendo veramente, ma dopo aver giocato un po 'con la risposta precedente ho trovato un'altra soluzione, forse più appropriata:

<system.webServer>
<modules>
    <remove name="UrlRoutingModule-4.0" />
    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
</modules>
</system.webServer>

Grazie, anche io non so cosa stia facendo, ma sembra migliore dell'altra soluzione.
Thomas

1
sembra spostare il modulo di instradamento URL (che è ciò che cattura il fatto che la richiesta include un'estensione e quindi cerca di gestirla come un file) alla fine dell'elenco dei moduli, il che è sufficiente per metterlo dopo il modulo che gestisce la richiesta api . IMHO, questa è la migliore soluzione alternativa disponibile tra quelle che ho visto su SO, almeno ATTOW
James Manning

1
Non è il trasferimento alla fine dell'elenco, è la rimozione della precondizione predefinita che questo modulo venga eseguito solo per i gestori gestiti. La configurazione predefinita utilizza questo formato:<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler" />
theta-fish

Grazie - questo spiegherebbe il comportamento.
Greg Z.23

8

Ho scoperto che dovevo fare di più che impostare l' runAllManagedModulesForAllRequestsattributo su true. Ho anche dovuto assicurarmi che il gestore di URL senza estensione fosse configurato per esaminare tutti i percorsi. Inoltre, è possibile aggiungere un'altra impostazione di configurazione bonus che aiuterà in alcuni casi. Ecco il mio Web.config funzionante:

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

Nota, in particolare, che il ExtensionlessUrlHandler-Integrated-4.0suo pathattributo è impostato su *invece di *.(per esempio).


Sono appena stato morso dal path="*.". Solo curioso, qual è il motivo per cui le persone lo impostano path="*."?
JustinP8

In realtà, l'ho cambiato in path="*"e ho avuto un problema perché ospitiamo un sito di documentazione accanto alla nostra WebAPI e quel sito aveva problemi con .jpg, .png e altri file con estensioni.
JustinP8

@ JustinP8 Hai anche impostato <modules runAllManagedModulesForAllRequests="true" />? Ciò dovrebbe fare in modo che .NET gestisca quei file statici.
Josh M.

1
Sì. Ho finito per farlo. Non mi piace molto però perché ora tutti i file statici passano attraverso la pipeline .NET. Fortunatamente, poiché si tratta di un servizio webAPI, sono interessati solo gli elementi Swagger e Swashbuckle per i documenti API / sito di supporto.
JustinP8

Ho scoperto che questo ha rotto tutte le richieste di file statici. errore 500. Avevo runAllManaged ... impostato su true.
Sam

2

Sono rimasto bloccato in questa situazione, ma l'aggiunta / alla fine dell'URL non mi è sembrata pulita.

quindi aggiungi sotto nel tag web.config handlers e sarai a posto.

<add name="Nancy" path="api" verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" allowPathInfo="true" />

Puoi spiegarci cos'è Nancy e se si tratta di una libreria da inserire nel progetto? Grazie!
bluastro

1

Ho scoperto che entrambi i modi funzionano per me: impostare runAllManagedModulesForAllRequests su true o aggiungere ExtentionlessUrlHandler come segue. Infine scelgo di aggiungere extensionUrLHandler poiché runAllManagedModulesForAllRequests ha un impatto sulle prestazioni del sito.

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

1

Lo userei nel file Web.config:

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

prima dello standard "ExtensionlessUrlHandler".

Ad esempio nel mio caso lo metto qui:

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

Quindi costringi gli URL di tale pattern a essere gestiti da te, invece della gestione standard come file nella struttura della directory dell'applicazione.


0

Ho affrontato lo stesso problema e le circostanze in cui mi trovavo in cui non avrei dovuto giocare con IIS e le impostazioni relative alla configurazione del sito web. Quindi ho dovuto farlo funzionare apportando modifiche solo a livello di codice.

Il punto semplice è che il caso più comune in cui si finirebbe per avere un punto nell'URL è quando si riceve un input dall'utente e lo si passa come stringa di query o frammento di URL per passare alcuni argomenti ai parametri nel metodo di azione del tuo controller.

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

Dai un'occhiata all'URL sottostante in cui l'utente inserisce il suo ID utente per ottenere i suoi dettagli personali:

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

Dato che devi semplicemente recuperare alcuni dati dal server, utilizziamo il GETverbo http . Durante l'utilizzoGET chiamate, qualsiasi parametro di input può essere passato solo nei frammenti di URL.

Quindi, per risolvere il mio problema, ho cambiato il verbo http della mia azione in POST. Il POSTverbo http ha la possibilità di passare anche qualsiasi input utente o non utente nel corpo. Quindi ho creato un dato JSON e l'ho passato nel corpo della POSTrichiesta http :

{
  "userid" : "foo.bar"
}

Modificare la definizione del metodo come di seguito:

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

Nota : Maggiori informazioni su quando usare il GETverbo e quando usare il POSTverbo qui .


Non è una soluzione appropriata se stai creando un'API REST.
Mustafa Ozturk
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.