views:

987

answers:

4

Hi,

I need to host a WCF Data service as part of a legacy ASP.NET MVC website running in IIS 7 Integrated mode.

Read access to the web service is working fine, but whenever I try to POST to the web service, I get a 400 Bad Request error.

For testing purposes, I have downloaded the sample odata service from http://www.odata.org/developers/odata-sdk#/media/7582/odatasampleservices.zip. Posting to that webservice works fine when running it in a separate IIS website. However, when I put it in my legacy website, I am geting that 400 Bad Request error wen sending a post request to the service again.

Therefore the problem seems to be related to the configuration of my ASP.NET MVC project. However, I am at a loss on how to proceed.

This is the web config file I am using in my legacy project:

<?xml version="1.0" encoding="utf-8"?>
<!-- 
 Note: As an alternative to hand editing this file you can use the 
 web admin tool to configure settings for your application. Use
 the Website->Asp.Net Configuration option in Visual Studio.
 A full list of settings and comments can be found in 
 machine.config.comments usually located in 
 \Windows\Microsoft.Net\Framework\v2.x\Config 
-->
<configuration>
 <configSections>
 <section name="combres" type="Combres.ConfigSectionSetting, Combres" />
 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net" />
 <sectionGroup name="elmah">
  <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
  <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
  <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
  <section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
  <section name="errorMailSAZ" requirePermission="false" type="ElmahFiddler.ElmahMailSAZSectionHandler, ElmahFiddler" />
 </sectionGroup>
 </configSections>

 <combres definitionUrl="~/App_Data/combres.xml" />
 <log4net>
 <root>
  <level value="ALL" />
  <appender-ref ref="RollingFile" />
 </root>
 <logger name="Combres">
  <level value="DEBUG" />
 </logger>
 <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
  <file value="log.txt" />
  <appendToFile value="true" />
  <maximumFileSize value="100KB" />
  <maxSizeRollBackups value="2" />
  <layout type="log4net.Layout.PatternLayout">
  <conversionPattern value="%d [%t] %-5p %c - %m%n" />
  </layout>
 </appender>
 </log4net>
 <appSettings configSource="appSettings.config" />
 <connectionStrings configSource="connectionstrings.config">

 </connectionStrings>
 <!-- Mail server settings-->
 <system.net>
    <mailSettings/>
 </system.net>
 <system.web>
 <globalization uiCulture="de" culture="de-DE" />

 <compilation debug="true">
  <assemblies>
  <add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  <add assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
  <add assembly="Microsoft.ReportViewer.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
  <add assembly="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  <add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  <add assembly="System.Data.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  </assemblies>
  <buildProviders>
  <add extension=".rdlc" type="Microsoft.Reporting.RdlBuildProvider, Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </buildProviders>
 </compilation>

 <authentication mode="Forms">
  <!-- User muessen sich nach 30 Tagen abwesenheit neu einloggen (wenn remember me angeklickt wird)-->
  <forms loginUrl="~/Profile/LogOn" timeout="43200" slidingExpiration="true" />
 </authentication>
 <membership>
  <providers>
  <clear />
  <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="500" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" applicationName="/" />
  </providers>
 </membership>
 <profile>
  <providers>
  <clear />
  <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" applicationName="/" />
  </providers>
 </profile>
 <roleManager enabled="true">
  <providers>
  <clear />
  <add connectionStringName="ApplicationServices" applicationName="/" name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  <add applicationName="/" name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </providers>
 </roleManager>
 <customErrors configSource="customErrors.config" />
 <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID">
  <namespaces>
  <add namespace="System.Web.Mvc" />
  <add namespace="System.Web.Mvc.Ajax" />
  <add namespace="System.Web.Mvc.Html" />
  <add namespace="System.Web.Routing" />
  <add namespace="System.Linq" />
  <add namespace="System.Collections.Generic" />
  <add namespace="xVal.Html" />
  <add namespace="Zeiterfassung.Views" />
  <add namespace="Zeiterfassung.Models" />
  <add namespace="Zeiterfassung" />
  <add namespace="GrigoreComponents.Generic" />
  <add namespace="DeverMind.Generic" />
  <!--<add namespace="MvcContrib.UI.Grid.ActionSyntax" />-->
  </namespaces>
 </pages>
 <httpHandlers>
  <add path="captcha.ashx" verb="GET" type="ManagedFusion.Web.Mvc.Handlers.CaptchaImageHandler, ManagedFusion, Version=1.0.3490.29346, Culture=neutral" validate="false" />
  <add path="*.mvc" verb="*" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" validate="false" />
  <!--<add path="VSEnterpriseHelper.axd" verb="GET" type="Microsoft.VisualStudio.Enterprise.Common.AspNetHelperHandler, Microsoft.VisualStudio.Enterprise.ASPNetHelper, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />-->
  <add path="Reserved.ReportViewerWebControl.axd" verb="*" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate="false" />
  <add path="/elmah.axd" verb="POST,GET,HEAD" type="Elmah.ErrorLogPageFactory, Elmah" />
 </httpHandlers>
 <httpModules>
  <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
 </httpModules>
 </system.web>
 <system.web.extensions>
 <scripting>
  <webServices>
  <authenticationService enabled="true" requireSSL="false" />
  </webServices>
 </scripting>
 </system.web.extensions>
 <!-- 
  The system.webServer section is required for running ASP.NET AJAX under Internet
  Information Services 7.0. It is not necessary for previous version of IIS.
 -->
 <system.webServer>

 <validation validateIntegratedModeConfiguration="false" />
 <modules runAllManagedModulesForAllRequests="true">
  <add name="Elmah.ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
  <add name="Elmah.ErrorFilter" type="Elmah.ErrorFilterModule" preCondition="managedHandler" />
  <add name="Elmah.ErrorMail" type="Elmah.ErrorMailModule" preCondition="managedHandler" />
  <add name="elmahSAZ" type="ElmahFiddler.ElmahMailSAZTraceModule, ElmahFiddler" />
 </modules>
 <handlers>
  <add name="Elmah" path="elmah.axd" verb="POST,GET,HEAD" type="Elmah.ErrorLogPageFactory, Elmah" preCondition="integratedMode" />
  <remove name="MvcHttpHandler" />
  <remove name="UrlRoutingHandler" />
  <add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />
  <!--<add name="VSEnterpriseHelper.axd" verb="GET" path="VSEnterpriseHelper.axd" preCondition="integratedMode" type="Microsoft.VisualStudio.Enterprise.Common.AspNetHelperHandler, Microsoft.VisualStudio.Enterprise.ASPNetHelper, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>-->
  <add name="ReportViewerWebControlHandler" preCondition="integratedMode" verb="*" path="Reserved.ReportViewerWebControl.axd" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
 </handlers>
 <!--404 fehler sind was besonderes, ich will sie nicht in elmah sehen und ausserdem funktioniert es aus unbekannten gruenden eh nicht wenn ich sie auf dem hetzner-server per 
 customerrors handle-->
 <httpErrors>
  <remove statusCode="404" subStatusCode="-1" />
  <error statusCode="404" prefixLanguageFilePath="" path="/Error/FileNotFound" responseMode="ExecuteURL" />
 </httpErrors>
 <staticContent>
  <!--cache static content on the client side for 7 days-->
  <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
 </staticContent>
 <rewrite>
  <rules>
  <rule name="CanonicalHostName-Adrian" enabled="true" stopProcessing="true">
   <match url="(.*)" />
   <conditions>
   <add input="{HTTP_HOST}" pattern="^worklogger\.de:4567$" />
   </conditions>
   <action type="Redirect" url="http://www.worklogger.de:4567/{R:1}" />
  </rule>
  <rule name="CanonicalHostName-Sandra" enabled="true" stopProcessing="true">
   <match url="(.*)" />
   <conditions>
   <add input="{HTTP_HOST}" pattern="^worklogger\.de:7654$" />
   </conditions>
   <action type="Redirect" url="http://www.worklogger.de:7654/{R:1}" />
  </rule>
  <rule name="CanonicalHostName-Production" enabled="true" stopProcessing="true">
   <match url="(.*)" />
   <conditions>
   <add input="{HTTP_HOST}" pattern="^logmytime\.de$" />
   </conditions>
   <action type="Redirect" url="http://www.logmytime.de/{R:1}" />
  </rule>
  <rule name="RemoveTrailingSlashRule1" enabled="true" stopProcessing="true">
   <match url="(.*)/$" />
   <conditions>
   <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
   <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
   </conditions>
   <action type="Redirect" url="{R:1}" />
  </rule>
  <rule name="Rewrite rule for RewriteMap" enabled="true" stopProcessing="true">
   <match url=".*" />
   <conditions>
   <add input="{RewriteMap:{REQUEST_URI}}" pattern="(.+)" />
   </conditions>
   <action type="Redirect" url="{C:1}" appendQueryString="false" />
  </rule>
  <rule name="jquery-UI images" enabled="true" stopProcessing="true">
   <match url="(.*)combres.axd/UserAreaCSS/images/(.*)" />
   <conditions logicalGrouping="MatchAny"></conditions>
   <action type="Rewrite" url="Scripts/jquery-ui/css/custom-theme/images/{R:2}" />
  </rule>
    <rule name="Add API trailing slash without redirecting the user to a different domain" enabled="false" patternSyntax="ExactMatch" stopProcessing="true">
     <match url="API/V1/APi.svc" />
     <action type="Redirect" url="API/V1/APi.svc/" redirectType="Permanent" />
    </rule>
  </rules>
  <rewriteMaps>
  <rewriteMap name="RewriteMap">

   <add key="/Zeiterfassung/zeiterfassungssoftware.html" value="/Zeiterfassung/Arbeitszeit/Zeiterfassungssoftware" />
  </rewriteMap>
  </rewriteMaps>
 </rewrite>
 </system.webServer>
 <runtime>
 <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependentAssembly>
  <assemblyIdentity name="Microsoft.VisualStudio.Enterprise.ASPNetHelper" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
  <codeBase version="9.0.0.0" href="file:///C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio%209.0/Common7/IDE/PrivateAssemblies/Microsoft.VisualStudio.Enterprise.ASPNetHelper.DLL" />
  </dependentAssembly>
  <dependentAssembly>
  <assemblyIdentity name="Microsoft.VisualStudio.Enterprise.ASPNetHelper" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
  <codeBase version="9.0.0.0" href="file:///C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio%209.0/Common7/IDE/PrivateAssemblies/Microsoft.VisualStudio.Enterprise.ASPNetHelper.DLL" />
  </dependentAssembly>
  <dependentAssembly xmlns="">
  <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
  <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0" />
  </dependentAssembly>
 </assemblyBinding>
 </runtime>
 <location path="VSEnterpriseHelper.axd">
 <system.web>
  <authorization>
  <allow users="?" />
  </authorization>
 </system.web>
 </location>
 <!-- Deny everyone except siteadmins to see the elmah.axd -->
 <location path="elmah.axd">
 <system.web>
  <authorization>
  <allow roles="SiteAdmin" />
  <deny users="*" />
  </authorization>
 </system.web>
 </location>
 <system.serviceModel>
 <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
 </system.serviceModel>
</configuration>

Thanks for your help,

Adrian

Edit: Not sure if it's any helpe, but here's the fiddler output and the failed request trace:

> curl -u login:pass
> "http://www.worklogger.de:4567/Testapi/Testapi.svc/Projects"
> -v -d test
> * About to connect() to www.worklogger.de port 4567 (#0)
> *   Trying 192.168.0.109... connected
> * Connected to www.worklogger.de (192.168.0.109) port 4567 (#0)
> * Server auth using Basic with user 'login'
> POST /Testapi/Testapi.svc/Projects HTTP/1.1
> Authorization: Basic bG9naW46cGFzcw==
> User-Agent: curl/7.19.0 (i686-suse-linux-gnu) libcurl/7.19.0 OpenSSL/0.9.8h zlib/1.2.3 libidn/1.10
> Host: www.worklogger.de:4567
> Accept: */*
> Content-Length: 4
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 400 Bad Request
< Cache-Control: private
< Server: Microsoft-IIS/7.5
< X-AspNet-Version: 4.0.30319
< Set-Cookie: ASP.NET_SessionId=arrujp3wazcugm55v4ysyuew; path=/; HttpOnly
< X-Powered-By: ASP.NET
< Date: Sun, 27 Jun 2010 13:24:05 GMT
< Content-Length: 0
<
* Connection #0 to host www.worklogger.de left intact
* Closing connection #0

I saved the failed request trace to pastebin to http://pastebin.com/K3uEZfVS since StackOverflow does not allow posts to grow over 50k.

Edit2: I would like to point out again that the very same web service runs fine (both POST and GET!) when putting it in a brandnew ASP.NET MVC website with no other changes. Therefore the problem cannot be related to the way I am making the request.

A: 
  • Have you tried setting aspNetCompatibilityEnabled to false, in the web.config you posted above?

If the corresponding attribute is not set in the code of the WCF service(it's default is AspNetCompatibilityRequirementsMode.NotAllowed) the service denies calls from any client that has aspNetCompatibilityEnabled set to true.

  • If that setting was the problem, here is the long term fix, I have added the attribute [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] to my WCF service class
GenEric35
I've tried adding this attribute to my WCF Data Service, but it does not seem to have any effect. The 400 Bad Request Error is still there.
Adrian Grigore
in my case for the attribute to take effect I had to SelfHost the service(doing the serviceHost.Open() manually instead of IIS doing it on demand). An easy way to rule out the compatibilty problem is to call the service while aspNetCompatibilityEnabled is set to false, is it possible for you to do that?
GenEric35
on second thought my case was slightly different, the aspNetCompatibilityEnabled=true was in the same application as the WCF service, and that prevented the client from getting an answer back
GenEric35
can you step through in debug mode? there should be faults or exceptions thrown
GenEric35
and does "http://www.worklogger.de:4567/Testapi/Testapi.svc/Projects" or "http://www.worklogger.de:4567/Testapi/Testapi.svc" open in in the browser? or is there a yellow page of death with a call stack, exception, inner exception?
GenEric35
this error has also been reported for people with low values in their binding configs, my typical values are maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
GenEric35
GenEric: There are no exceptions or faults when debugging the application and posting to the web service. POST requests do not even trigger the InitializeService method.
Adrian Grigore
Also, please see Edit 2 above.
Adrian Grigore
ok, you tested with a new client, this one on a new mvc web site, any chance the difference between the two is that your old one has <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> in it's web.config?
GenEric35
No. As I said, the MVC project is brandnew, vanilla web.config. In fact even the brandnew project has this in the web.config: <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />. So it really seems that this does not have anything to do with this directive.
Adrian Grigore
can you please post the configuration of the client and service?
GenEric35
@GenEric: I had already posted the command line input / output for curl and the complete web.config. in my edit above.
Adrian Grigore
there is no binding or endpoint configuration, no exception... and only web server logs, can't quite see the problem
GenEric35
The problem is that the request fails with a 400 Bad Request error. No update is performed on the database. The wcf data service does not even get invoked.
Adrian Grigore
A: 

Which O/S is this service hosted on ?
If its Vista or above, you can use FREB to decode what failed in the pipeline. http://learn.iis.net/page.aspx/266/troubleshooting-failed-requests-using-tracing-in-iis-7/ Can you run Fiddler to identify what the HTTP error response from the server is ?

Phani Raj
Thanks for trying to help, but I already posted the failed request trace plus the transcript from curl in my OP.
Adrian Grigore
Adrian, FREB tracing is different from HTTP tracing using CURL. CURL is tracing the HTTP packet sent by the server to the client.FREB tracing would debug the iis worker process to give you a reason as to why the request failed with a HTTP 400 Bad Request.
Phani Raj
less logs, more WCF...
GenEric35
@Phani: I do understand what Failed request tracing is. If you read the OP closely, you will notice a link to http://pastebin.com/K3uEZfVS , which shows exactly what you were asking for - a failed request trace.
Adrian Grigore
A: 

I worked around the problem by implementing a proxy to the web service: http://code.google.com/p/fastajaxproxy/

Adrian Grigore
A: 

Disable WebDav in IIS. It Worked for me... Most at times, post methods are used to perform delete or put operations. Disabling this enables post to perform delete and put operations

Solomon
I haven't knowingly enabled WebDav in my project. Is this something that would be enabled by default?
Adrian Grigore