tags:

views:

85

answers:

1

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.

+1  A: 

Check that related handler for .svc in IIS is allowed to process HTTP POST, PUT, DELETE. In case of problems with only PUT and DELETE check that your virtual directory is not configured for WebDAV (I think it is some HTTP module).

Ladislav Mrnka
I checked the handler mappings for *.svc and it was set to allow all verbs. So that should be ok. How do I check the latter (whether or not virtual directory is configured for WebDAV?) This is in my test environment on my dev machine, so the location of the application hosted in IIS is actually my SVN source project folder. Permissions issue maybe?
Joshua Hayes
This is strange at least HTTP POST should work without any problem unless you access wrong URL. WebDav is used only if you installed it (it is windows feature under IIS). If virtual directory is configured to use WebDav it should contain WebDavModule in its module configuration (check it in IIS management console).
Ladislav Mrnka
Good call, after noticing that WebDav was installed I went to 'add remove features' and uninstalled that module from IIS. It appears to have resolved my aforementioned put issues. I'll write up some more tests to check the other Http verb types tomorrow.I'm marking your answer as fixed so thank you kindly. Are you able to explain why WebDav caused this to occur so I know in future? Once again, thanks.
Joshua Hayes
I think PUT and DELETE are verbs originally used for WebDAV. Once WebDAV is installed and configured for application (default) it always handle these verbs as WebDAV requests.
Ladislav Mrnka
Ok. Thanks very much!
Joshua Hayes