Returning Plain old XML with WCF - vb.net

How can this be so difficult and why? Returning simple XML with no Name Spaces and an XML decaration. Creating the XML using XML Writer is easily done and if I output it to a file then great. How on earth can you return the contents via WCF. Using an XML Element is no good as you loose the XML declaration and using a string is no good as the output is wrapped up in a <string> element. Can't return an XML Document as it can't be serialised.
I know that there are many posts on this site, but none answer the question. I am using VB.NET (boy I wish I had the time to learn C#) and I cannot get the Data Contract to work either even using IXmlSerializer. An example of the output I need to send back via WCF service is:
<?xml version="1.0" encoding="utf-8"?>
<BookingResponse timestamp="" success="1">
<confirmation id="" track_code="" status="" notes="" tracking_url="" confirmed_at=""/>
</BookingResponse>
Unfortunately the service I am sending to will not accept anything other than this plain XML. The will not accept additional Name Spaces that WCF seems to put in all by it's self.
I am using the Online Rest Template for VB that is available for Visual Studio 2010.
Help! Please! someone must have an answer?
Here is some code:
<WebInvoke(UriTemplate:="BookJob", Method:="POST")>
Public Function Create(ByVal XMLBooking As Stream) As Stream
'DO SOME PROCESSING ON THE REQUEST - THIS ALL WORKS FINE.....
Dim str As String
'This CreateResponseXML function creates a String version of the XML, which is built using XMLWriter.
str = CreateResponseXML(True, vConfirmationID, sqlFuncs.BookingStatus("Confirmed"), "", "")
Dim memoryStream As New MemoryStream()
Using streamWriter As New StreamWriter(memoryStream)
streamWriter.Write(str)
streamWriter.Flush()
memoryStream.Position = 0
Return memoryStream
End Using
End Function
To add to the hassel they have to use a secure connection. I am using the Online Rest Template for VB that is available for Visual Studio 2010 which cuts down the entries in the web.config significantly compared to the normal one I see everywhere. It looks like this
:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<customErrors mode="Off"/>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</modules>
</system.webServer>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<bindings>
<webHttpBinding>
<binding transferMode="Streamed">
<security mode="Transport" />
</binding>
</webHttpBinding>
</bindings>
<standardEndpoints>
<webHttpEndpoint>
<!--
Configure the WCF REST service base address via the global.asax.vb file and the default endpoint
via the attributes on the <standardEndpoint> element below
-->
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true" faultExceptionEnabled="true">
<security mode="Transport" />
</standardEndpoint>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
</configuration>
I tried returning a MemoryStream previous to this code, but the response they got was wrapped in a MemoryStream element and was gobbldegook (but this may have been due to the fact that I ommited the transferMode="Streamed" entry in the web.config. This version returns nothing at all.
Any Ideas?

Use WCFRestContrib it has POX formatter (Plain Old Xml) which is clean and without any namespaces.
The POX formatter uses the
System.Runtime.Serialization.DataContractSerializer, but unlike the
WCF REST Contrib xml formatter ([Discussed under Xml Formatter
Overview), does not serialize data contract namespaces or xml schema
attributes, it does not require namespaces to be specified in xml to
be deserialized and it does not require elements to be in a specific
order. This enables you to serve and accept very simple xml.
See POX Formatter Overview

Use Stream as the return value.

Related

Content Type application/soap+xml; charset=utf-8 was not supported by service

I am getting the error below while trying to add WCF service to WCFTestClient. I went through a number of solutions on the web but I couldn't get it to work.
Can someone help me with the issues?
I am also providing my config file for service:
Content Type application/soap+xml; charset=utf-8 was not supported by
service The client and service bindings may be mismatched. The
remote server returned an error: (415) Cannot process the message
because the content type 'application/soap+xml; charset=utf-8' was not
the expected type 'text/xml; charset=utf-8
Code:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<!-- When deploying the service library project, the content of the config file
must be added to the host's app.config file. System.Configuration does not
support config files for libraries. -->
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicHttp" allowCookies="true"
maxReceivedMessageSize="20000000"
maxBufferSize="20000000"
maxBufferPoolSize="20000000">
<readerQuotas maxDepth="32"
maxArrayLength="200000000"
maxStringContentLength="200000000"/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="WCFTradeLibrary.TradeService">
<endpoint address="" binding="basicHttpBinding"
bindingConfiguration="basicHttp"
contract="WCFTradeLibrary.ITradeService">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint
above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faul`enter code here`ts for
debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception info`enter code here`rmation -->
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
I run into naming problem. Service name has to be exactly name of your implementation. If mismatched, it uses by default basicHttpBinding resulting in text/xml content type.
Name of your class is on two places - SVC markup and CS file.
Check endpoint contract too - again exact name of your interface, nothing more. I've added assembly name which just can't be there.
<service name="MyNamespace.MyService">
<endpoint address="" binding="wsHttpBinding" contract="MyNamespace.IMyService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
Here is the example of a web.config that solve the issue for me. Pay attention on the <binding name="TransportSecurity" messageEncoding="Text" textEncoding="utf-8">
In my case one of the classes didn't have a default constructor - and class without default constructor can't be serialized.
I had the same problem, got it working by "binding" de service with the service behaviour by doing this :
Gave a name to the behaviour
<serviceBehaviors>
<behavior name="YourBehaviourNameHere">
And making a reference to your behaviour in your service
<services>
<service name="WCFTradeLibrary.TradeService" behaviorConfiguration="YourBehaviourNameHere">
The whole thing would be :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicHttp" allowCookies="true"
maxReceivedMessageSize="20000000"
maxBufferSize="20000000"
maxBufferPoolSize="20000000">
<readerQuotas maxDepth="32"
maxArrayLength="200000000"
maxStringContentLength="200000000"/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="WCFTradeLibrary.TradeService" behaviourConfiguration="YourBehaviourNameHere">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicHttp" contract="WCFTradeLibrary.ITradeService">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="YourBehaviourNameHere">
<!-- To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faul`enter code here`ts for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception info`enter code here`rmation -->
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
As suggested by others this happens due to service client mismatch.
I ran into the same problem, when was debugging got to know that there is a mismatch in the binding. Instead of WSHTTPBinding I was referring to BasicHttpBinding. In my case I am referring both BasicHttp and WsHttp. I was dynamically assigning the binding based on the reference. So check your service constructor as shown below
Refer this image
This error may occur when WCF client tries to send its message using MTOM extension (MIME type application/soap+xml is used to transfer SOAP XML in MTOM), but service is just able to understand normal SOAP messages (it doesn't contain MIME parts, only text/xml type in HTTP request).
Be sure you generated your client code against correct WSDL.
In order to use MTOM on server side, change your configuration file adding messageEncoding attribute:
<binding name="basicHttp" allowCookies="true"
maxReceivedMessageSize="20000000"
maxBufferSize="20000000"
maxBufferPoolSize="20000000"
messageEncoding="Mtom" >
My case had a different solution.
The client was using basichttpsbinding[1] and the service was using wshttpbinding.
I resolved the problem by changing the server binding to basichttpsbinding.
Also, i had to set target framework to 4.5 by adding:
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5"/>
</system.web>
[1] the comunication was over https.
For me, it was very difficult to identify the problem because of a large number of methods and class involved.
What I did is commented (removed) some methods from the WebService interface and try, and then comment another bunch of methods and try, I kept doing this until I found the method that cause the problem.
In my case, it was using a complex object which cannot be serialized.
Good luck!
I experienced the same error message. I managed to fix it:
In my case the error was that I missed the [datacontract] and [datamember] attributes in the parent class of my returned class. The error message was misleading.
[OperationContract]
List<MyClass> GetData();
[DataContract]
public class MyClass : MyParentClass
{
[DataMember]
public string SomeString { get; set; }
}
// Missing DataContract
public class MyParentClass
{
// Missing DataMember
public int SomeNumber { get; set; }
}
In my case same error was caused by missing
[DataContract]
...
[DataMember]
attributes in the returned data type.
Check for that and try addinging those and see if it helps.
I too had the same error in trace logs. My newly created function in API was throwing the same error but to my surprise, the old functions were performing good.
The issue was - My contract data members had few variables of type object. soap-xml was not able to handle it well, however, I can see that array of object types (object[]) were getting passed without any issues. Only a simple object type was not getting parsed by soap. This could be one more reason why the services throw the above error.
My problem was our own collection class, which was flagged with [DataContract]. From my point of view, this was a clean approach and it worked fine with XmlSerializer but for the WCF endpoint it was breaking and we had to remove it. XmlSerializer still works without.
Not working
[DataContract]
public class AttributeCollection : List<KeyValuePairSerializable<string, string>>
Working
public class AttributeCollection : List<KeyValuePairSerializable<string, string>>
I was getting same error while using WebServiceTemplate spring ws
[err] org.springframework.ws.client.WebServiceTransportException: Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'. [415]
[err] at org.springframework.ws.client.core.WebServiceTemplate.handleError(WebServiceTemplate.java:665).
The WSDL which i was using has soap1.2 protocol and by default the protocol is soap1.1
.
When i changed the protocol using below code, it was working
MessageFactory msgFactory = MessageFactory.newInstance(javax.xml.soap.SOAPConstants.SOAP_1_2_PROTOCOL);
SaajSoapMessageFactory saajSoapMessageFactory = new SaajSoapMessageFactory(msgFactory);
saajSoapMessageFactory.setSoapVersion(SoapVersion.SOAP_12);
getWebServiceTemplate().setMessageFactory(saajSoapMessageFactory);
I had the same problem and solved it by using EnumMemberAttribute for enum member's attribute. If you are using enum type as data contract and its members attributed with DataMemberAttribute, same error occurs. You must use EnumMemberAttribute for members of enum
I also came across the same problem. In my case, I was using transfermode = streaming with Mtom. As it turns out, I had named one of my variables (for a structure), "HEADER". This conflicted with the message element [http://tempuri.org/:HEADER] as part of the http service download. Clearly, one must avoid using "reserved" words as parameter name.
I had to add the ?wsdl parameter to the end of the url.
For example: http://localhost:8745/YourServiceName/?wsdl
check the client config file that identified to the web.config on the binding section

Handling REST SWT tokens in WCF WIF pipeline is not working

I'm struggling on a setup mentioned in the subject line and am wondering if someone can help me.
Essentially, what I have is a WCF service and I want to achieve that the user can authenticate against the ACS using a custom login page (using the javascript with required information from ACS).
After doing that the user should get redirected to the WCF service using the provided SWT token. I am using the SimpleWebTokenHandler as a basis for the SWT token handling, but I'm not sure it's playing any role in this.
Here's the Web.config I'm running
<configuration>
<configSections>
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral" />
</configSections>
...
<system.serviceModel>
<diagnostics>
</diagnostics>
<services>
<service name="WcfWifSwtAcs.Service1">
<endpoint address="xmlService" binding="webHttpBinding" bindingConfiguration="" behaviorConfiguration="restPoxBehaviour" name="xmlServiceEndpoint" contract="WcfWifSwtAcs.IService1" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="restPoxBehaviour">
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials useIdentityConfiguration="true">
...
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add scheme="http" binding="ws2007FederationHttpBinding" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<bindings>
<ws2007FederationHttpBinding>
<binding name="">
<security mode="Message">
<message
issuedTokenType="http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0">
<issuerMetadata address="https://xxxx.accesscontrol.windows.net/v2/wstrust/13/certificate/mex" />
</message>
</security>
</binding>
</ws2007FederationHttpBinding>
</bindings>
</system.serviceModel>
<system.webServer>
...
</system.webServer>
<system.identityModel>
<identityConfiguration>
<audienceUris>
<add value="http://localhost:56782/Service1.svc" />
</audienceUris>
<issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<trustedIssuers>
<add thumbprint="XXX" name="xxx.accesscontrol.windows.net" />
</trustedIssuers>
</issuerNameRegistry>
<issuerTokenResolver type="SimpleWebToken.CustomIssuerTokenResolver, WcfWifSwtAcs" />
<securityTokenHandlers>
<clear/>
<add type="SimpleWebToken.SimpleWebTokenHandler, WcfWifSwtAcs"/>
</securityTokenHandlers>
</identityConfiguration>
</system.identityModel>
</configuration>
Now I can see, that the authentication happens and that the browser is redirected with the body to the service. I can also see that the SimpleWebToken handler get's instantiated and the token type URI is being requested. But that's almost all that happens. No actual token handling verification and whatsoever is happnening.
This is the token that get's sent to the service (after parsing).
wa=wsignin1.0&
wresult=
<t:RequestSecurityTokenResponse
xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:Lifetime>
<wsu:Created
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2013-02-13T23:14:30.159Z</wsu:Created>
<wsu:Expires
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2013-02-13T23:24:30.159Z</wsu:Expires>
</t:Lifetime>
<wsp:AppliesTo
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<EndpointReference
xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://localhost:56782/Service1.svc</Address>
</EndpointReference>
</wsp:AppliesTo>
<t:RequestedSecurityToken>
<wsse:BinarySecurityToken
wsu:Id="uuid:58e2fb15-dd1a-40bd-8ff0-ae24e22e6efe"
ValueType="http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0"
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
BASE64 DATA==
</wsse:BinarySecurityToken>
</t:RequestedSecurityToken>
<t:TokenType>http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0</t:TokenType>
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
</t:RequestSecurityTokenResponse>
Service itself is braindead simple, with following signature.
[OperationContract]
[WebInvoke(UriTemplate = "/GetData/{id}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
string GetData(string id);
Any ideas? I've been verifying that the uri's, hostnames, thumbprints etc. are all valid. Also the service tracing doesn't really show up anything that is related either to token handling or exceptions in the token verification.
Somehow it almost seems that the token doesn't get even passed to the handler. At least all the claims and other authentication information is missing (null).
I would appreciate if someone points me to a direction of either where can I debug or if I'm missing something really obvious (which might also always be the case).
P.S. I know I could achieve it with custom authentication modules and whatsoever, I'd rather get it running with WIF (it's becoming fundamental as I've spend more time on this as I really wanted and I'm very stubborn :p).
Soo, dedication will bring one to a solution. Although I initially thought that this can't be done, it's apparent that it actually can. I'll put the solution here, as maybe there are other people who find it useful.
First of all, WCF REST services are using webHttpBinding, which according to MS documentation does not support the Windows Identity Foundation and claims handling in the pipeline. Actually it does. Not in the WCF pipeline, but as the IIS module in web authentication flow.
First, you need to add the following modules to Web.config file.
<system.webServer>
<modules runManagedModulesForAllRequests="true">
<add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" preCondition="managedHandler" />
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" preCondition="managedHandler" />
</modules>
</system.webServer>
There's a caveat tho. You need still the <configSections> from my original posting. The problem is that you need, in VisualStudio, to mark the System.IdentyModel* assemblies as CopyLocal items (in the properties window). Otherwise you'll get some cryptic exception that assembly cannot be loaded for the configuration section. NB! It only happens if you are loading these two modules and doesn't happen when those modules are not getting loaded. Didn't have any will to investigate that thing further, perhaps someone knows better what's the cause there.
Next if for any reason you plan to use the SWT token handling sample from MS WIF code, there are a couple of bugs that need to be fixed, otherwise the token parsing just won't happen or you will get invalid signatures out of the token verification.
SimpleWebToken.cs you need to fix the SwtBaseTime as it is initialized incorrectly and the security token creation fails afterwards:
From
public static DateTime SwtBaseTime = new DateTime( 1970, 1, 1, 0, 0, 0, 0 ); // per SWT psec
To
public static DateTime SwtBaseTime = new DateTime( 1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc ); // per SWT psec
SimpleWebTokenHandler.cs you need to fix the casing of the following values:
From
const string BinarySecurityToken = "binarySecurityToken";
const string ValueType = "valueType";
To
const string BinarySecurityToken = "BinarySecurityToken";
const string ValueType = "ValueType";
CustomIssuerTokenResolver.cs you need to fix the key that is created as it's initalized with a UTF8 bytes, but it should actually get initialized with decoded Base64 bytes:
From
key = new InMemorySymmetricSecurityKey(UTF8Encoding.UTF8.FromBase64String(base64Key));
To
key = new InMemorySymmetricSecurityKey(System.Convert.FromBase64String(base64Key));
After you've fixed all this, everything sits in place. The authenticators and authorizators are getting called and voilĂ , suddenly you have a WCF Service exposed as REST endpoint and all the claims etc. are also working.
I think your issue may be with the SWTTokenHandler in this sample: http://code.msdn.microsoft.com/vstudio/Custom-Token-ddce2f55
In CanReadToken(), it checks to see if the token is a BinarySecurityToken of type SWT:
if ( reader.IsStartElement( BinarySecurityToken )
&& ( reader.GetAttribute( ValueType ) == SimpleWebTokenConstants.ValueTypeUri ) )
But the constant BinarySecurityToken is defined as:
const string BinarySecurityToken = "binarySecurityToken";
Note the lower-case "b". XML elements are case sensitive, and the actual element is "BinarySecurityToken" with a capital B. This will cause the handler to return false in CanReadToken(), causing WIF to believe it doesn't have a handler registered for this token type.

Very delayed IIS response from WCF service when returning fairly large data set

I have an IIS 7 hosted WCF service that is mainly being used to return data through the entity framework. One of the operation contracts/service methods returns a list of roughly 17000 very simple and fairly small entity objects. The response size ends up being roughly 6.5MB so it's not huge. When I host the service on my development machine with IIS Express, the service call is made and the data is posted back promptly (within 10 seconds). When I push the service to our server, the response takes an average of 1:46 seconds to come back. After doing some tracing on the web server, I discovered that the method that fetches the data only takes 6 seconds to return. That being the case, the server is taking roughly 1:40 seconds to prepare and then send the response (which I've confirmed in the web logs so it's not a network latency issue).
These are the only configurations that I have in the service definition:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
This is what my web.config looks like:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<httpRuntime executionTimeout="600" />
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="RGDataBehavior" name="WCFData.Web.Services.DataAccess">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="LargeBuffer" name="RGData_http" contract="WCFData.Web.Services.IDataAccess" listenUriMode="Explicit"/>
<host>
<baseAddresses>
<add baseAddress="http://myurl.com/Services/Data.svc"/>
</baseAddresses>
</host>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="LargeBuffer"
closeTimeout="00:10:00"
openTimeout="00:10:00"
receiveTimeout="00:10:00"
sendTimeout="00:10:00"
transferMode="StreamedResponse"
maxBufferPoolSize="2147483647"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxStringContentLength="2147483647"
maxNameTableCharCount="2147483647"/>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="RGDataBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceAuthorization
serviceAuthorizationManagerType="WCFData.Web.Authentication.WCFAuthenticator,
Data.Web.Authentication"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
I've been busting my head over this and most of what I've found doesn't quite seem to fit the bill so I would love some help if anyone has suggestions. Thanks in advance.
I would recommend enabling WCF trace logging which might help get more timing numbers out of the WCF internals.
You could also, run the server through a profiling tool, and try to narrow down exactly what method is taking so long, but I'm not sure if there is a good way to do that through IIS.
If all else fails, you can always take some thread dumps while the server is processing (during this 1m40s processing time) and look at the stack trace for each thread to see what might be stuck. I usually do that using ntsd.exe command line debugger that installs with the Debugging Tools for Windows package.
Edit:
Here is another idea. Since you think it is related directly to the serialization, you can isolate the DataContractSerializer and time how long it takes to serialize your data. Code would look like:
[OperationContract]
public YourData[] YourServiceMethod()
{
YourData[] data = ... // get all your data here as usual.
// lets serialize it and time how long it takes
var timer = new System.Diagnostics.Stopwatch();
timer.Start();
var dataContractSerializer = new System.Runtime.Serialization.DataContractSerializer(data.GetType());
using (var memoryStream = new System.IO.MemoryStream())
{
dataContractSerializer.WriteObject(memoryStream, data);
}
timer.Stop();
System.Console.WriteLine(timer.ElapsedMilliseconds); // Log this, or whatever...
// return now as normal (WCF will re-serialize, talking even longer, but this is just a test anyway)
return data;
}
The answer to my question is more of a work around than a true solution to the problem. Changing the service method to return a byte array rather than a collection so that IIS wouldn't have to write an XML serialized collection into the response took care of the issue but it still doesn't explain why the delay was happening in the first place. I used the protobuf-net library to serialize my return collection to a byte array and then again on the client side to deserialize the array.

WCF .NET 4 OutputCaching with Stream doesn't seem to work

I'm having problems with OutputCaching over a WCF REST service on .NET4-IIS7. My service has a custom Authorization scheme (by implementing ServiceAuthorizationManager), one which should take place on every request, and any caching must be done after the request is authorized. So far, this seems to work, only the caching part I can't see happening.
I have the following OperationContract:
[OperationContract]
[AspNetCacheProfile("PageZIP")]
[WebGet(UriTemplate = "pages/{page_id}")]
System.IO.Stream getPage(string page_id);
And the following web.config:
<system.web>
<compilation targetFramework="4.0" />
<customErrors mode="Off" />
<authentication mode="None" />
<caching>
<outputCache enableOutputCache="true"/>
<outputCacheSettings>
<outputCacheProfiles>
<add name="PageZIP" duration="7200" location="ServerAndClient"
varyByParam="page_id" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceAuthorization serviceAuthorizationManagerType="api.Authorization, api" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint transferMode="StreamedResponse" automaticFormatSelectionEnabled="false" defaultOutgoingResponseFormat="Json" />
</webHttpEndpoint>
</standardEndpoints></system.serviceModel>
I can see the new response headers filled with client-side cache information when I call the service, but caching at the server doesn't seem to work. My custom log shows that my api.Authorization class is (rightly) being called, but my getPage() method is also executing as normal, up to the point where my file is being .OpenRead() into a Stream and returned:
public System.IO.Stream getPage(string page_id) {
File zip = FileMapper.getZip(pid);
ctx.OutgoingResponse.Headers.Clear();
ctx.OutgoingResponse.Headers.Add("Content-Disposition", "attachment; filename=" + page_id + ".zip");
ctx.OutgoingResponse.Headers.Add("Content-Length", zip.size.ToString());
ctx.OutgoingResponse.ContentType = "application/zip";
Log.write("OpenRead", LogType.Info);
return System.IO.File.OpenRead(zip.path);
}
If output is being cached, this method shouldn't be executed at all... I expect the Stream to be cached and be served directly, without queries to the database and disk reads. Every zipfile is about 1 MB in size.
What am I missing or doing wrong?
After playing with settings for a while, the answer came out: OutputCache won't work if transferMode is streaming, even if you implement your own OutputCacheProvider. The reason behind this is that, in a streamed response scenario, you're telling WCF not to buffer your response in memory, and try to read it from wherever it is and send it down to transport level. OutputCache depends on the object being fully in-memory before it's returned, so that WCF can keep the reference to it and put that on cache.
It's up to you to see in your scenario if enabling streaming without output cache is faster than reading and keeping the object in-memory so you can output it directly.

Setting custom WCF-binding behaviour via .config file - why doesn't this work?

I am attempting to insert a custom behavior into my service client, following the example here.
I appear to be following all of the steps, but I am getting a ConfigurationErrorsException. Is there anyone more experienced than me who can spot what I'm doing wrong?
Here is the entire app.config file.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="ClientLoggingEndpointBehaviour">
<myLoggerExtension />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="myLoggerExtension"
type="ChatClient.ClientLoggingEndpointBehaviourExtension, ChatClient, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<bindings>
</bindings>
<client>
<endpoint
behaviorConfiguration="ClientLoggingEndpointBehaviour"
name="ChatRoomClientEndpoint"
address="http://localhost:8016/ChatRoom"
binding="wsDualHttpBinding"
contract="ChatRoomLib.IChatRoom"
/>
</client>
</system.serviceModel>
</configuration>
Here is the exception message:
An error occurred creating the
configuration section handler for
system.serviceModel/behaviors:
Extension element 'myLoggerExtension'
cannot be added to this element.
Verify that the extension is
registered in the extension collection
at
system.serviceModel/extensions/behaviorExtensions.
Parameter name: element (C:\Documents
and Settings\Andrew Shepherd\My
Documents\Visual Studio
2008\Projects\WcfPractice\ChatClient\bin\Debug\ChatClient.vshost.exe.config
line 5)
I know that I've correctly written the reference to the ClientLoggingEndpointBehaviourExtensionobject, because through the debugger I can see it being instantiated.
This is a bit of a random thought, but maybe not: reverse the order of the elements in your config so extensions come before behaviors.
-Oisin
It turns out that I didn't get the assembly qualified name EXACTLY right.
The assembly qualified name was correct enough for the .NET framework to load, but then the WCF framework performs a naive character-by-character comparison when matching the behavior configurations.
To finally get the exact type name, I wrote code to create an instance of ClientLoggingEndpointBehaviourExtension object, and wrote the AssemblyQualifiedName property to a local variable, which I then copy-and-pasted from the debug window into the .config file.
That I had to do all this is considered to be a bug in the WCF framework.
(See this link)
Apparently it's fixed in .NET 4.0.
Also see this article.