How to enable IIS compression for WCF services? - wcf

I currently use a custom gzip encoder for my WCF service. I want to replace it with the built-in IIS 7 compression if that is possible. I can't find info online on how to that.
Is there any way to enable IIS 7 compression for WCF services?
Do you know if this will be supported out-of-the-box with .Net 4?
Edit June 15th: I'm still looking for a way to replace our custom gzip encoder with a mainstream approach so if you know how to do that with .Net 4 and IIS 7, please answer this question.
Thanks
Sidebar : My attempt at doing this manually
Since I can't find how to do it by simply turning a few knobs I decided to try and enable it manually.
So far I have:
Installed and enabled the IIS 7 Dynamic Compression Module
Changed the section of the applicationHost.config file to enable compression for mimeType="application/soap+xml" and mimeType="application/xop+xml".
I used an HTTP sniffer to sniff traffic sent from my app (Windows Forms). I see that requests do not have the Accept-Encoding:gzip,deflate http header.
So I
Added it manually to all outgoing calls using the OperationContextScope class and its OutgoingMessageProperties. (I will post the details later if I find the solution).
With the http sniffer, I can see that the client header now has the correct header:
POST /### path to my service ####/MyService.svc HTTP/1.1
MIME-Version: 1.0
Content-Type: multipart/related; type="application/xop+xml";
start="<http://tempuri.org/0>";
boundary="uuid:####### some uuid #############";
start-info="application/soap+xml"
Accept-Encoding: gzip,deflate
Host: ####### my server name #############
Content-Length: 1753
Expect: 100-continue
But the server response is still not compressed.
Why is the server response not compressed? Have I used the correct mime types? Once I get the server to return a compressed answer, will the client automatically decompress it or will have to write code on the client side to decompress?
Thanks for your help

I had the same problem; .aspx pages were compressed but WCF content wasn't. It has to do with the content type returned by the WCF service, which got appended to the mime-type.
I got it to work with the following section in the ApplicationHost.config:
<dynamicTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/*" enabled="true" />
<add mimeType="*/*" enabled="false" />
</dynamicTypes>
Here's what I did (most of the same steps as mentioned already):
Install the Dynamic Compression Role Service for IIS role
Enable Dynamic Content Compression for the website that you use to host the WCF service
Open %SystemRoot%\system32\inetsrv\config\applicationhost.config and add the relevant content type to the section of the
After this it still didn't work.
I checked the data with Firefox' Tamper Data and noticed the content type returned was actually "application/xml; charset=utf-8".
After adding the complete content type, including the "; charset=utf-8" to the section, it worked:
<add mimeType="application/xml; charset=utf-8" enabled="true" />
As I felt that the character set encoding should not be determining if the compression works or not, I ended up letting IIS compress all application/* content types.

This is useful for IIS 6
http://ramon.bloggingabout.net/2008/11/06/wcf-and-http-gzipdeflate-compression-and-silverlight/
(updated URL)

Perhaps it depends on the specific WCF service setup you are using, but for the applications I have used it in (all were mixed access for both .NET applications and Silverlight pages), the generated WCF client class contained an EnableDecompression property that can be set to true. After that my Winforms apps send the correct headers and the webservice communication is correctly compressed.

It seems you can enable Dynamic Compression in IIS via the GUI or CLI.
This article shows you both ways:
http://www.hanselman.com/blog/EnablingDynamicCompressionGzipDeflateForWCFDataFeedsODataAndOtherCustomServicesInIIS7.aspx
I found the GUI way easy. The article shows you how to confirm it is working with Fiddler.
Cheers!

Related

Missing "Access-Control-Allow-Methods" header for CORS in IIS Express

I know similar questions have been asked many times but after hours of using any suggestion I can find it still does not resolve.
This is ASP.Net Core 3.1 web app which serves a web API, which I need to have accessible in development with Visual Studio - usual debug mode which runs IIS Express. Another web app is accessing the API (both on localhost, but different port numbers). GET and POST works, PUT does not work.
I know that no preflight requests get sent for GET, HEAD and POST requests, so the error seems to occur only when IIS (or Asp.Net?) has to handle the preflight request.
Browser shows the error
Access to ... from origin ... has been blocked by CORS policy: Method PUT
is not allowed by Access-Control-Allow-Methods in preflight response.
Both the preflight request and the actual PUT request are being performed, visible in the browser, but fail.
The response headers of the preflight request contain:
access-control-allow-headers: *
access-control-allow-origin: *
They NEVER contain an "access-control-allow-methods" header, no matter what I tried. It seems obvious that the PUT request won't work because the preflight request SHOULD contain this header with "PUT" specified as allowed method.
What I Tried
Controlling CORS from AspNetCore web app
(This would require that the preflight request actually reaches the app and isn't caught by IIS and never forwarded - which I don't know, however)
Installed Microsoft.AspNetCore.Mvc.Cors package, then in Startup.cs:
services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://localhost:8080").AllowAnyMethod().AllowAnyHeader();
});
});
plus the corresponding UseCors() entry lower in Startup.cs (after Routing and before Authorization, as it should be).
Also tried
services.AddCors();
in services, and then the following in the Configure section:
app.UseCors(x => x
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(origin => true) // allow any origin
.AllowCredentials()); // allow credentials
Same result - no "access-control-allow-methods" header.
IIS Express configuration
In case IIS is doing the preflight job, it should be configurable directly there.
Curiously, IIS Express has 5 locations with configuration files:
C:\Users<username>\Documents\IISExpress\config\
C:\Program Files\IIS Express (x86)\config\templates\PersonalWebServer\
C:\Program Files\IIS Express (x86)\AppServer\
C:\Program Files\IIS Express\config\templates\PersonalWebServer\
C:\Program Files\IIS Express\AppServer\
None of these contained anything related to CORS in my case (but the two headers shown above nevertheless show). So I tried adding custom headers:
<httpProtocol>
<customHeaders>
<clear />
<add name="access-control-allow-origin" value="*" />
<add name="access-control-allow-headers" value="*" />
<add name="access-control-allow-methods" value="GET,POST,PUT,PATCH,DELETE,OPTIONS" />
</customHeaders>
<redirectHeaders>
<clear />
</redirectHeaders>
</httpProtocol>
I added these to EVERY "applicationhost.config" file in above locations. Same result - nothing changes.
web.config in AspNet Core web app
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost:8080" />
<add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE" />
<add name="Access-Control-Allow-Headers" value="*" />
Same result.
(I did restart the computer after every one of these changes, just for good measure.)
At this stage, I am out of options.
Why would 3 different ways to configure CORS have literally NO EFFECT on what the server sends at all?
Is there a way to find out whether the preflight request gets handled by IIS or by ASP.NET Core? At least then I would know where I have to put the configuration.
Thanks to the comment of LexLi I found my own answer.
My use case was, as mentioned for Visual Studio debugging to work with CORS, and for that, there is yet another location for a IIS-Express configuration file:
$(SolutionDir)\.vs\$ProjectName)\config
I put the above-mentioned custom header lines into this file (plus an additional "access-control-max-age" header).
This alone did not work - the C# UseCors() code in the web app was needed as well. Then it works perfectly.
So in fact my code was all correct - just missing know-how on VS.
I agree with Lex Li that one should rather use the IIS CORS module - yet this has to be configured and IIS Express does not provide a config tool.

Fiddler HTTP/1.1 401 Unauthorized

I have local Web API project which is NOT running through local IIS. To run the project I use F5 in Visual Studio 2013.
Using Fiddler, I keep getting:
HTTP/1.1 401 Unauthorized
# Result Protocol Host URL Body Caching Content-Type Process Comments Custom 88 401 HTTP localhost:52787 /api/values 6,180 private text/html; charset=utf-8 fiddler:10724
I know I am supposed to get 200 but I am not. Where should I check what I am doing wrong?
Here is a screenshot from a browser. I am getting this when I go back to Fiddler to see the results, I just type http://localhost:52787/api/helloapi into URL (in a browser) and press enter:
And here is what I get when I go through Fiddler manually composing GET:
I have the option Automatically Authenticate checked.
When you say "using Fiddler", what exactly do you mean?
If you are manually composing the request using Fiddler's Composer, either add an Authorization header yourself, or click the Composer's Options tab and check the Automatically Authenticate box.
I just had the same Problem. In my case it was caused by a deny clause in the web.config that forced all users to be authenticated. This works well in a browser because this handles the authentication behind the scenes and sends an appropriate authorization Cookie. In Fiddler this handshake does not take place and hence the 401. In the development environment add this to your web.config and it should work.
<system.web>
<authorization>
<allow users="?" />
</authorization>
</system.web>
I know this is an older post but if someone is still looking for an answer (like me) and the above answers did not resolve the problem then try this solution -
The above answers didn't work in my dev env which consists of VS 2017 and VSTS as I already had the <authorization> element set correctly in my web.config file. After a few hit and trials I figured that the source of the problem was somewhere else.
I needed to make the following change to "applicationhost.config" file which can be usually found under the path "C:\Users\xxx\Source\Repos\yyy\yyy\.vs\config" -
Locate the <anonymousAuthentication> element and make sure the "enabled" attribute is set to "true" as under:
<system.webServer>
<security>
<authentication>
<anonymousAuthentication enabled="true" />
<windowsAuthentication enabled="true" />
</authentication>
</security>
</system.webServer>
It happens because you have an Authorize attribute on your ValuesController
[Authorize]
public class ValuesController : ApiController
Just remove [Authorize] attribute on ValuesController

Error while accessing the core service on SDL Tridion 2011 SP1

I am getting an error while accessing the core service on SDL Tridion 2011 SP1. When I am trying to browse /webservices/CoreService2011.svc from IIS server, it shows the following error:
This collection already contains an address with scheme http.
There can be at most one address per scheme in this collection. If your service is being hosted in IIS you can fix the problem by setting 'system.serviceModel/serviceHostingEnvironment/multipleSiteBindingsEnabled' to true or specifying 'system.serviceModel/serviceHostingEnvironment/baseAddressPrefixFilters'.
Parameter name: item
Can any one help, how it can be rectified.
I believe you have multiple hostnames setup for your Tridion CME. Or at least you are trying to connect to your Content Manager (in this case with Core Service) using multiple hostnames.
Can you try the following:
connect using localhost (obviously when you are local on the server) E.g. http://localhost/webservices/CoreService2011.svc
If above doesn't work, try looking up what host name is registered in IIS for your SDL Tridion 2011 website (in IIS 7, Right click the website, then choose Edit Bindings...). Try connect to the Core Service using the hostname defined in the website bindings
If above still doesn't solve it, try editing your web.config under "Tridion_Home\webservices" and add the following node under configuration / system.ServiceModel
Node:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
<!-- The attribute "multipleSiteBindingsEnabled" was introduced in .net 4 and removes the need of http module: Tridion.Web.ServiceModel.HttpSvcPortFunneler -->
<!-- For https protocol and/or multiport configuration, uncomment this.
There should be a <add /> entry for each unique combination of protocol and hostname that is configured in IIS Bindings.
<baseAddressPrefixFilters>
<add prefix="http://hostname:portnumber"/>
<add prefix="https://hostname"/>
</baseAddressPrefixFilters>
-->
</serviceHostingEnvironment>

Access-Control Origin HTTP header on WSO2 ESB Out

How do I set custom HTTP headers on an ESB proxy service Out Sequence? I'm trying to set "Access-Control-Allow-Origin" to allow my javascript/html front-end page to be able to GET the XML that is provided by this service. I also need to add a Cache-Control.
If there is a way to do this directly on my WSO2 Data Services Server (DSS), that would be preferable as it would avoid adding an ESB server to my process. According to this forum post from about a year ago, it's not possible: http://wso2.org/forum/thread/13991
I've tried it several ways, but looking at fiddler, the header is unchanged:
HTTP/1.1 200 OK
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 637
Date: Wed, 28 Mar 2012 20:58:31 GMT
Server: WSO2 Carbon Server
I'm somewhat new with WSO2 servers (more of a front-end dev), so the answer could be right in front of me.
You can do this by adding a Property mediator to the out-sequence. Once you set the property with the transport scope there, it will be added to the transport header of the out going message from the ESB.
This property mediator worked for me:
<property name="Access-Control-Allow-Origin" value="*" scope="transport" type="STRING"></property>
It allows access from any origin.
-Kari

Incompatibility between InfoPath 2007 WebServiceConnection and WCF

I am trying to post data from InfoPath using the WebServiceConnection, to a WFC service inside of AppFabric.
The messgae never arrives in AppFabric, and I think I know why.
My WCF service is configured like this
<endpoint address="Workflow1.xamlx" binding="basicHttpBinding" contract="WorkflowOperation" />
And when it is called by WCF Test Client, it generates the header
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/WorkflowOperation/ReceiveFormPayload</a:Action>
</s:Header>
However, InfoPath does not generate this Soap header, it only generates the HTTP header
POST /Workflow1.xamlx HTTP/1.1
SOAPAction: "http://tempuri.org/WorkflowOperation/ReceiveFormPayload"
Content-Type: text/xml; charset="UTF-8"
User-Agent: SOAP Toolkit 3.0
Host: localhost:51842
Content-Length: 1893
Connection: Keep-Alive
Cache-Control: no-cache
How do I configure my WCF endpoint to only need the HTTP-header action, and not the Soap Action?
Well, I'm back again, with the answer for anyone who is interested in getting InfoPath to submit data to an AppFabric service.
The .NET BasicHttpBinding uses a combination of Soap1.2+WSAddressing1.0 - while the InfoPath client will ONLY submit to web services using Soap1.1 (with NO WSAddressing support) - rending the two completely incompatible.
I actually ended up having to write an intermediary broker which would adapt the invocations.
Many thanks to "codemeit" for a very descriptive page of the composition of the various WCF binding types.