2010-01-28 6 views
8

Ho un controller che accetta solo un post su questo URL:Come utilizzare un vincolo personalizzato con HttpMethodConstraint nel routing ASP.NET MVC?

POST http://server/stores/123/products 

Il POST dovrebbe essere di tipo di contenuto application/json, quindi questo è quello che ho nella mia tabella di routing:

routes.MapRoute(null, 
       "stores/{storeId}/products", 
       new { controller = "Store", action = "Save" }, 
       new { 
         httpMethod = new HttpMethodConstraint("POST"), 
         json = new JsonConstraint() 
        } 
       ); 

dove JsonConstraint è:

public class JsonConstraint : IRouteConstraint 
{ 
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) 
    { 
     return httpContext.Request.ContentType == "application/json"; 
    } 
} 

Quando uso il percorso, ho un 405 Forbidden:

The HTTP verb POST used to access path '/stores/123/products' is not allowed

Tuttavia, se rimuovere il vincolo json = new JsonConstraint(), funziona benissimo. Qualcuno sa cosa sto sbagliando?

+0

favore potete pubblicare il tuo frammento di jQuery? Ho eseguito alcuni test e mostra che ContentType viene mostrato come "application/xml". –

+0

Sto usando un plugin per Firefox chiamato 'REST Client' per testarlo. –

risposta

8

Lo inserisco in un commento ma non c'è abbastanza spazio.

Durante la scrittura di un vincolo personalizzato è molto importante controllare il parametro routeDirection e assicurarsi che la logica funzioni solo al momento giusto.

Questo parametro indica se il vincolo viene eseguito durante l'elaborazione di una richiesta in entrata o in esecuzione mentre qualcuno sta generando un URL (ad esempio quando chiama Html.ActionLink).

Nel tuo caso penso che si desidera inserire tutto il codice corrispondente in un gigante "se":

public bool Match(HttpContextBase httpContext, Route route, 
    string parameterName, RouteValueDictionary values, 
    RouteDirection routeDirection) 
{ 
    if (routeDirection == RouteDirection.IncomingRequest) { 
     // Only check the content type for incoming requests 
     return httpContext.Request.ContentType == mimeType; 
    } 
    else { 
     // Always match when generating URLs 
     return true; 
    } 
} 
+0

Buona risposta, ma non dovremmo usare 'Accept' invece di' ContentType'? http://stackoverflow.com/a/15898503/1804678 – Jess

+0

@Jess la domanda originale menzionata specificamente 'ContentType'. Il 'ContentType' indica il formato del corpo della richiesta, mentre l'intestazione' Accept' indica il * desiderato * formato dal corpo della risposta. – Eilon

4

Vorrei eseguire il debug del JsonConstraint e vedere qual è il tipo di contenuto.

È possibile che, per qualsiasi motivo, non sia application/json.

So che questo è il tipo MIME RFC, ma ne ho visti alcuni altri in giro nel mio tempo (come ad esempio text/x-json), come è stato menzionato qui in uno previous question.

Inoltre, non ho mai visto un vincolo ContentType, quindi sarei interessato a vedere se funziona. L'hai provato con altri tipi MIME nel caso in cui fosse difettoso?

Infine, anziché disporre di un solo JsonConstraint, creerei un generico ContentTypeConstraint.

Aggiornamento:

Bussai insieme un metodo WebRequest rapido su un percorso che utilizza il codice ContentTypeConstraint, e che sembra funzionare correttamente.

Enum

public enum ConstraintContentType 
{ 
    XML, 
    JSON, 
} 

classe Constraint

public class ContentTypeConstraint : IRouteConstraint 
{ 
    private string mimeType; 

    public ContentTypeConstraint(ConstraintContentType constraintType) 
    { 
    //FYI: All this code could be redone if you used the Description attribute, and a ToDescription() method. 
    switch (constraintType) 
    { 
     case ConstraintContentType.JSON: 
     mimeType = "application/json"; 
     break; 
     case ConstraintContentType.XML: 
     mimeType = "text/xml"; 
     break; 
     default: 
     mimeType = "text/html"; 
     break; 
    } 
    } 

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) 
    { 
    //As suggested by Eilon 
    if (routeDirection == RouteDirection.UrlGeneration) 
     return true; 

    return httpContext.Request.ContentType == mimeType; 
    } 
} 

Questo sarebbe chiamato, con il tuo esempio, come:

contentType = new ContentTypeConstraint(ConstraintContentType.JSON) 

Questo è stato il vincolo è riutilizzabile per mu ch più che solo JSON. Inoltre, il caso switch può essere eliminato se si utilizzano gli attributi di descrizione nella classe enum.

+0

Grazie per l'aggiornamento e il post sul blog. –

+0

@Jess Lo è. Purtroppo ho buttato giù il mio blog qualche anno fa, ma la carne e le ossa della risposta sono ancora valide (anche se piuttosto vecchie). –

Problemi correlati