2013-01-14 14 views
25

W moim projekcie interfejsu API sieci Web nie mogę wykonać HTTP PUT dla moich zasobów. Przeczytałem przez some thisproblem i zastosowałem się do zalecanej porady.HTTP PUT nie jest dozwolone w ASP.NET Web API

Po pierwsze, całkowicie odinstalowałem WebDAV na moim komputerze (Windows 7 64-bit), a następnie ponownie uruchomiłem komputer.

drugie, koparki WebDAV zostały określone jako usunięte w moim web.config i HTTP PUT czasownik został określony jako dozwolony dla bez rozszerzeń URL Handler.

<modules runAllManagedModulesForAllRequests="false"> 
    <remove name="WebDAVModule"/> 
</modules> 

<handlers> 
    <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" /> 
    <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" /> 
    <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> 
    <remove name="WebDAV"/> 
    <add name="ExtensionlessUrlHandler-Integrated-4.0" 
     path="*." 
     verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" 
     type="System.Web.Handlers.TransferRequestHandler" 
     resourceType="Unspecified" 
     requireAccess="Script" 
     preCondition="integratedMode,runtimeVersionv4.0" /> 
    <add name="AttributeRouting" path="routes.axd" verb="*" type="AttributeRouting.Web.Logging.LogRoutesHandler, AttributeRouting.Web" /> 
</handlers> 

Próbowałem nawet dodając URL bez rozszerzeń ISAPI Handler (32-bit i 64-bit) oraz zmianę mojej aplikacji z App Pool zintegrowanego rurociągu do klasycznej App Pool.

<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" 
     path="*." 
     verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" 
     modules="IsapiModule" 
     scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" 
     preCondition="classicMode,runtimeVersionv4.0,bitness32" 
     responseBufferLimit="0" /> 
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" 
     path="*." 
     verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" 
     modules="IsapiModule" 
     scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" 
     preCondition="classicMode,runtimeVersionv4.0,bitness64" 
     responseBufferLimit="0" /> 

obecnie używam Thinktecture IdentityModel włączyć Cross-Origin Resource Sharing (CORS) wsparcie. Jeśli chodzi o moje zdrowie psychiczne, wybrałem opcję jądrową, która umożliwi wszystko, aby upewnić się, że rzeczywiście jest dozwolona HTTP PUT.

config.RegisterGlobal(httpConfig); 

config.ForAllResources() 
     .ForAllOrigins() 
     .AllowAllMethods() 
     .AllowAllRequestHeaders(); 

Attribute Routing pakiet Nuget jest skonfigurowany do odbioru wszystkich tras z obecnej montażu i wszelkie podtypy ApiController.

config.AddRoutesFromAssembly(Assembly.GetExecutingAssembly()); 
config.AddRoutesFromControllersOfType<ApiController>(); 

Mój zasób ma również poprawnie określony atrybut PUT.

[PUT("/API/Authenticate/Link/{key}/{identifier}")] 
public Boolean LinkUser(Guid key, String identifier) { ... } 

Każdy zasób Patrzę na ten temat zaleca dokładnie taki sam rzecz: Odinstalowywanie WebDAV, wyłączanie ładowarki WebDAV i upewniając się, że procedura obsługi bez rozszerzeń URL jest prawidłowo skonfigurowany. Zrobiłem to wszystko i nie działa nadal.

W Skrzypek, Dostaję następujące:

PUT https://localhost/Test/API/Authenticate/Link/Foo/Bar 

{"Message":"The requested resource does not support http method 'PUT'."} 

Co robię źle?

+0

że atrybut powinien być '[HttpPut]'? –

+0

@dtryon: Nie. Pochodzi z 'AttributeRouting'. Powinien to być "[PUT (...)]". –

+0

Miałem ten problem - było tak, ponieważ moja akcja kontrolera nie została poprawnie skonfigurowana - oczekiwał parametru, którego nie zawierałem. – niico

Odpowiedz

19

Podobno istnieje znany problem w obrębie AttributeRouting, w którym metody HttpPut są obecnie niefunkcjonalne w interfejsie API sieci ASP.NET.

currently accepted workaround to dodać odpowiedni czasownik na trasie aż odpowiednia poprawka przychodzi:

Web API RC uszczelnione istotną interfejs do wykrywania trasy przez bazowego ramach . Chociaż interfejs jest teraz publiczny, zmiana nie zostanie zwolniona przed vNext. Oto kilka obejść:

  • Użyj atrybutów AR w połączeniu z atrybutami HttpGet, HttpPost, HttpPut lub HttpDelete z System.Web.Http:
[GET("some/url"), HttpGet] 
public string Method1() {} 

[PUT("some/url"), HttpPut] 
public string Method2() {} 

[POST("some/url"), HttpPost] 
public string Method3() {} 

[DELETE("some/url"), HttpDelete] 
public string Method4() {} 
+0

właśnie o tym napisałem, +1 –

+0

@FilipW: Zajęło mi to sporo czasu, aby to znaleźć. Umieściłem komentarz do linku w mojej odpowiedzi z pytaniem, czy byłoby możliwe udokumentowanie tego w kodzie (np. 'ObsoleteException'). Jest to dość mylący błąd, biorąc pod uwagę, że istnieją już dwa znane całkowicie niezwiązane problemy, które powodują ten sam problem. –

4

Miałem ten sam błąd i prześledzić je do trasy niestandardowej że będę zdefiniowany tak:

config.Routes.MapHttpRoute(
    name: "SomeCall", 
    routeTemplate: "api/somecall/{id}", 
    defaults: new { controller = "SomeCall", action = "Get" } 
); 

Problemem tutaj jest action = "Get" który zapobiegł akcji PUT tego samego identyfikatora URI, aby odpowiedzieć. Usunięcie domyślnego działania rozwiązało problem.

+0

Dzięki @MikeBantegui. Podczas badania problemu, twoje pytanie było najbardziej wyczerpujące i najbliższe mojej konfiguracji - z wyjątkiem Routingu Atrybutów - więc pomyślałem, że podam moje rozwiązanie, na wypadek, gdyby pomogło komuś innemu – Andrew

+0

Wystarczająco uczciwe. Czy masz pojęcie, dlaczego domyślną akcją było zapobieganie akcji 'PUT'? Wygląda na to, że może to być błąd. –

+0

Zastanawiam się, czy to też może być błąd - tak naprawdę nie można go nazwać "defaults", jeśli aktualna metoda Request nie może go przekroczyć! Chociaż przyznaję, że nie przyjrzałem się temu, dlaczego – Andrew

11

dwukrotnie sprawdzić, czy używasz [HttpPut] z System.Web.Http.

W pewnych okolicznościach może skończyć się za pomocą atrybutu z System.Web.Mvc.

To skutkuje 405s dla nas.

+1

Zrobiłem lewę, dziękuję! – Rob

+1

Zły, zły Resharper. – DontVoteMeDown

+0

czy mógłbyś podać mi więcej szczegółów? –

3

Co pracował dla mnie było, aby dodać atrybut trasy, jak miałem już zdefiniowany dla żądania GET, który był przeciążony, jak poniżej:

// GET api/Transactions/5 
    [Route("api/Transactions/{id:int}")] 
    public Transaction Get(int id) 
    { 
     return _transactionRepository.GetById(id); 
    } 

    [Route("api/Transactions/{code}")] 
    public Transaction Get(string code) 
    { 
     try 
     { 
      return _transactionRepository.Search(p => p.Code == code).Single(); 
     } 
     catch (Exception Ex) 
     { 
      System.IO.File.WriteAllText(@"C:\Users\Public\ErrorLog\Log.txt", 
       Ex.Message + Ex.StackTrace + Ex.Source + Ex.InnerException.InnerException.Message); 
     } 

     return null; 
    } 

więc dodałem do PUT:

// PUT api/Transactions/5 
    [Route("api/Transactions/{id:int}")] 
    public HttpResponseMessage Put(int id, Transaction transaction) 
    { 
     try 
     { 
      if (_transactionRepository.Save(transaction)) 
      { 
       return Request.CreateResponse<Transaction>(HttpStatusCode.Created, transaction); 
      } 
     } 
     catch (Exception Ex) 
     { 
      System.IO.File.WriteAllText(@"C:\Users\Public\ErrorLog\Log.txt", 
       Ex.Message + Ex.StackTrace + Ex.Source + Ex.InnerException.InnerException.Message); 
     } 

     return Request.CreateResponse<Transaction>(HttpStatusCode.InternalServerError, transaction); 
    } 
+1

Zrobiłem to/{id: int}. dzięki – Sameer

+0

Nie ma za co –

0

Myślę, że już tak nie jest, być może problem został już rozwiązany. ASP.NET MVC Web API umożliwia teraz $ http.put i tutaj jest kod do przetestowania. Kod

angularjs Script

$scope.UpdateData = function() { 
     var data = $.param({ 
      firstName: $scope.firstName, 
      lastName: $scope.lastName, 
      age: $scope.age 
     }); 

     $http.put('/api/Default?'+ data) 
     .success(function (data, status, headers) { 
      $scope.ServerResponse = data; 
     }) 
     .error(function (data, status, header, config) { 
      $scope.ServerResponse = htmlDecode("Data: " + data + 
       "\n\n\n\nstatus: " + status + 
       "\n\n\n\nheaders: " + header + 
       "\n\n\n\nconfig: " + config); 
     }); 
    }; 

kod HTML

<div ng-app="myApp" ng-controller="HttpPutController"> 
<h2>AngularJS Put request </h2> 
<form ng-submit="UpdateData()"> 
    <p>First Name: <input type="text" name="firstName" ng-model="firstName" required /></p> 
    <p>Last Name: <input type="text" name="lastName" ng-model="lastName" required /></p> 
    <p>Age : <input type="number" name="age" ng-model="age" required /></p> 
    <input type="submit" value="Submit" /> 
    <hr /> 
    {{ ServerResponse }} 
</form></div> 

ASP.NET MVC metoda działania kontrolera Web API

public class DefaultController : ApiController 
{ 

    public HttpResponseMessage PutDataResponse(string firstName, string lastName, int age) 
    { 
     string msg = "Updated: First name: " + firstName + 
      " | Last name: " + lastName + 
      " | Age: " + age; 

     return Request.CreateResponse(HttpStatusCode.OK, msg); 
    } 
} 

(Zmień adres URL, aby wysłać żądanie na) Po kliknięciu przycisku Wyślij, wysyła żądanie HttpPut do "/ api/default" (DefaultController), gdzie deklarowana jest metoda akcji PutDataResponse. Ta metoda zostanie wywołana, a użytkownik otrzyma odpowiedź.

Roztwór ten pierwotnie here

0

napisane dla mnie było, bo nie było ustawić typ nośnika w treści ciąg json na moją prośbę klienta http:

nowego StringContent (json, Encoding.UTF32, "application/json");

Wszelkiego rodzaju dziwne zachowanie, jeśli nie jest ustawione.