I have looked at nearly every single WCF Rest PUT/POST issues on SO here and have still been unable to determine why I am unable to PUT or POST to my web service but am able to call a test GetTime method via GET.
This particular service exchanges custom credential information (username, password and some other information) for a token. This token information is encrypted and added to the header of subsequent requests to other web services so that username/passwords don't have to be passed around everywhere.
All web service calls are still treated as stateless, but require this auth header information rather username/passwords to access secured service operations.
Contract
[ServiceContract(Namespace = "")]
public interface IUserService
{
[WebInvoke(Method = "PUT", UriTemplate = "users/{username}/session")]
[WebHelp(Comment = "Creates a new session for the specified username")]
[OperationContract]
AuthToken PutSession(string username, CustomCredential credential);
...
[WebInvoke(Method = "GET", UriTemplate = "time")]
[WebHelp(Comment = "Test method; returns the time")]
[RequireAuthToken]
[OperationContract]
DateTime GetTime();
}
Service Impelementation
public class UserService : IUserService
{
#region UserService Members
public AuthToken PutSession(string username, CustomCredential credential)
{
// ...
}
public DateTime GetTime()
{
return DateTime.Now;
}
}
Test Code
[Fact]
public void When_Authenticated_And_Getting_Time_Expect_200_Status()
{
// arrange
using (var client = Get_HttpClient_With_AuthHeaders()) {
// act
var result = client.Get("time");
// assert
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
}
}
The aforementioned GetTime works (just a test method I added).
[Fact]
public void When_PuttingSession_Expect_AuthToken_Is_Returned()
{
// arrange
using (var client = Get_HttpClient_With_No_Auth_Headers()) {
var cred = new CustomCredential("test", "password", 1);
var content = HttpContentExtensions.CreateDataContract<CustomCredential>(cred);
// act
HttpResponseMessage response = client.Put("users/test/session", content);
// assert
response.EnsureStatusIsSuccessful();
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
var authToken = response.Content.ReadAsDataContract<AuthToken>();
Assert.NotNull(authToken);
}
}
The above put returns a MethodNotAllowed (405).
Just as another test I added a Mex endpoint to the service, created a new console app and added a 'service reference' to this service. Using the generated client proxy code, I was able to something similar to....
IUserServiceClient client = new IUserServiceClient();
CustomCredential cred = ...
AuthToken token = client.PutSession("test_username", cred);
What this suggests is that...
- The web service is hosted in IIS correctly
- I am able to consume the service using SOAP client proxy generated code
- I am able to consume GET requests via more friendly HttpClient / rest toolkit
- I have tested this get in a browser and it works
- For some reason put and other related methods (post, delete. etc) do not work via rest toolkit
Have no idea what is causing this one.
EDIT
I also noticed that IIS must have created this web.config in the root of the website where the services are hosted which would seem to be allowing the main HTTP verbs.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<security>
<requestFiltering>
<verbs>
<add verb="PUT" allowed="true" />
<add verb="GET" allowed="true" />
<add verb="POST" allowed="true" />
<add verb="DELETE" allowed="true" />
</verbs>
<fileExtensions>
<add fileExtension=".svc" allowed="true" />
</fileExtensions>
</requestFiltering>
</security>
</system.webServer>
</configuration>
I have also checked in IIS 7 the handler mappings for *.svc and checked that 'all verbs' are enabled.