WCF - how to set "enableWebScript" dynamically? - wcf

I'm working on my first WCF service, which will support several Ajax calls. I have an endpoint configured this way:
<service behaviorConfiguration="ServiceBehavior" name="AQM">
<endpoint address="" behaviorConfiguration="web" binding="webHttpBinding" bindingConfiguration="Binding1" contract="IAQM" />
</service>
and my behavior configuration:
<endpointBehaviors>
<behavior name="web">
<webHttp />
<enableWebScript />
</behavior>
</endpointBehaviors>
I need to create my own error handling so that I can format some specific information back to the client (see here http://zamd.net/2008/07/08/error-handling-with-webhttpbinding-for-ajaxjson/). My WebServiceHostFactory looks like this:
public class MyServiceFactory : WebServiceHostFactory
{
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
var sh = new ServiceHost(typeof(AQM), baseAddresses);
sh.Description.Endpoints[0].Behaviors.Add(new WebHttpBehaviorEx());
return sh;
}
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return base.CreateServiceHost(serviceType, baseAddresses);
}
}
public class WebHttpBehaviorEx : WebHttpBehavior
{
protected override void AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
// Clear default error handlers
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Clear();
// Add our own error handler
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new ErrorHandlerEx());
}
However, after I created my own error handler, it seems it overrides the "enableWebScript" setting I had in my config above, which I think makes sense because now I'm creating my very own behavior dynamically which doesn't have any of the config settings above.
I read that this setting should be used with WCF/Ajax for security purposes (see here http://www.asp.net/ajaxlibrary/Using%20JSON%20Syntax%20with%20Ajax.ashx). So my question is, how can I set the the "enableWebScript" setting on my dynamically created behavior? Or is it not possible?
Update (6/1/2011): I'm also looking to dynamically change the behavior to use Windows credentials for authentication. In the config file it's done like this:
<bindings>
<webHttpBinding>
<binding name="Binding1">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</webHttpBinding>
</bindings>
This is another setting I need to make programmatically since it seems to ignore the config setting.

For me it worked after adding the following constructor in WebHttpBehaviorEx
public WebHttpBehaviorEx()
{
DefaultBodyStyle = WebMessageBodyStyle.Wrapped;
DefaultOutgoingRequestFormat = WebMessageFormat.Json;
DefaultOutgoingResponseFormat = WebMessageFormat.Json;
}

There is a class WebScriptEnablingBehavior that you should be able to create in instance of programmatically and add it to the Behaviors collection of your endpoint. I've never tried it, and don't know how exactly that would work having multiple behaviors defined on a single endpoint, but I think that's basically what you're doing in your declarative configuration. Unfortunately WebScriptEnablingBehavior (which inherits from WebHttpBehavior) is sealed, so you can't just inherit from it.
Update: (from here)
The WebScriptEnablingBehavior is a "profile" of the WebHttpBehavior functionality designed specifically for interop with ASP.NET AJAX clients. It adds in some AJAX-isms like the ability to automatically generate ASP.NET AJAX client proxies.
I'm not sure you actually need to use <enableWebScript/>, like Carlos said, it sounds like it's only needed when you're using ASP.NET AJAX.

Related

connect programmatically to a WCF service through HTTPS

I am working on a project that uses WCF service. I have built the service, configured the web.config file, deployed it on a IIS 7 server. The service is accesed through HTTPS (on my dev machine, i have self-created the certificate).
Everything is fine when a create the ServiceReference in Visual Studio 2010, it creates the client and it works fine.
What i need is to create a client programatically (need a little flexibility), so when i try to connect "manually", it gives me a error like this:
The provided URI scheme 'https' is invalid; expected 'http'.
Parameter name: via
The code for web.config is: (i hope there is nothing wrong in it)
<system.serviceModel>
<services>
<service name="WcfService1.Service1" behaviorConfiguration="WcfService1.Service1Behavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="TransportSecurity" contract="WcfService1.IService1" />
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WcfService1.Service1Behavior">
<serviceMetadata httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="TransportSecurity">
<security mode="Transport">
<transport clientCredentialType="None"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
The procedure i wrote to access the WCF service is:
void proc()
{
string ADRESASSL = "https://localhost/ServiciuSSLwsBind/Service1.svc";
WSHttpBinding bind= new WSHttpBinding();
EndpointAddress ea = new EndpointAddress(ADRESASSL);
var myChannelFactory = new ChannelFactory<IService1>(bind, ea);
IService1 client = null;
try
{
client = myChannelFactory.CreateChannel();
client.RunMethod1();
client.Close();
//((ICommunicationObject)client).Close();
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
if (client != null)
client.Close();
}
}
The code for IService1
[ServiceContract]
public interface IService1 : IClientChannel
{
[OperationContract]
int RunMethod1();
//....................................
}
It seems i am doing something wrong here, the procedure raises the Exception i mentioned. Something more i must do to work, but i didn't figured it out.
Thanks in advance for any advice you can give me.
I haven't tested this, but I believe you need to set the security mode for the binding before you create the factory. The default mode for security for WSHttpBinding is SecurityMode.Message, and you want SecurityMode.Transport.
You can resolve this one of three ways, as follows.
First, you can use the overloaded version of the WSHttpBinding constructor to specify the security mode, like this:
WSHttpBinding bind= new WSHttpBinding(SecurityMode.Transport);
bind.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
Secondly, you can use the parameterless constructor and specify the security mode (and the client credential type) like this:
WSHttpBinding bind= new WSHttpBinding();
bind.Security.Mode = SecurityMode.Transport;
bind.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
Third, you can place a binding configuration section in the client config and reference that section in the constructor, like this:
WSHttpBinding bind = new WSHttpBinding("TransportSecurity");
The third example assumes a wsHttpBinding section with the name "TransportSecurity" in the client config file.
For more information, check these MSDN articles:
How to: Set the Security Mode
WSHttpBinding Constructor
Well, solved the problem with the self created certificate.
I have changed the endpoint adress for both the programatically connection and the service reference in Viosual Studio 2010.
string ADRESASSL = "https://localhost/ServiciuSSLwsBind/Service1.svc";
now is
string ADRESASSL = "https://eu-pc/ServiciuSSLwsBind/Service1.svc";
I have changed the adress from localhost to the name of pc "eu-pc". It has to do with the domain the certificate was issued.
Using localhost or 127.0.0.1 worked only for one method or the other.
Hope this will help other guys who might run into this.

No endpoints found when adding a Custom ServiceHostFactory

I created a wcf application. I didn't change anything. Used the Service1.GetData(int). It works fine. I can hit the wsdl in a browser and everything. Then I created a custom service host factory that simply returns a new service host and the service never comes up. I can no longer get to the wsdl in a browser. I tried adding a Custom ServiceHost so I could do a little debugging and it appears that there are no endpoints being found (even when explicitly calling AddDefaultEndpoints(). This is true even when I explicitly add the endpoint to the web.config.
Does anyone have any ideas as to what the issue could be?
If anyone cares to take a look I put the code on github: https://github.com/devlife/Sandbox/tree/master/WcfService1
Why are you using ServiceHostFactory?
are you going to use AppFabric/IIS? or self hosted services?
I think you need to add a MEX endpoint.
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
Here is how I have defined a CustomHost in a project I am working on,
<%# ServiceHost Language="C#" Debug="true" Service="Servicename.Servicename" CodeBehind="Service1.svc.cs" Factory="WcfService1.CustomServiceHostFactory"%>
And this,
public class CustomServiceHostFactory : ServiceHostFactory
{
public CustomServiceHostFactory()
{
}
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new CustomServiceHost(serviceType, baseAddresses);
}
}
public class CustomServiceHost : ServiceHost
{
public CustomServiceHost()
{
}
public CustomServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
}
protected override void OnOpening()
{
base.OnOpening();
}
protected override void OnClosing()
{
base.OnClosing();
}
protected override void ApplyConfiguration()
{
base.ApplyConfiguration();
}
}
Note that the CustomServiceHost looks bare, but that is because my solution has a lot of logging and configuration in this CustomServiceHost that I removed and is not appropriate.
The other difference I can see also is that my CustomServiceHost does not add the endpoint. The endpoint is defined in the config file like this,
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Servicename.Servicename" behaviorConfiguration="ServiceBehavior">
<endpoint address="http://*******.svc" binding="wsHttpBinding" contract="Namespace.IContract" bindingConfiguration="BindingConfig">
</endpoint>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="BindingConfig" maxReceivedMessageSize="9291456">
<security mode="None">
</security>
<readerQuotas maxArrayLength="6291456" />
</binding>
</wsHttpBinding>
</bindings>

How to use ServiceRoutes while defining maxReceivedMessageSize for non-custom bindings in WCF

Editing this to refocus on the actual issue. I've preserved the origional question at the bottom of the message but changing the title and content to reflect what was really happening.
I need to override the maxReceivedMessageSize for a WCF service added to an MVC3 project via the ServiceRoute mechanism. Specifing the binding in the web.config doesn't work. How does one do this.
Initial question is below this line but is misleading based on lots of false positives I was seeing.
Hi I have used some examples to add a file streaming upload service to my MVC3 project. If I use the default bindings (i.e., not defined in web.config) the service works as long as I don't exceed the 64k default size. When I try and define my own binding to increase the size I get a content-type mismatch in my trace and a HTTP415 Unsupported Media Type in the response. I'm trying to call this via fiddler via HTTP and am not using a WCF client.
Here is the error in the trace:
Content Type image/jpeg was sent to a service expecting multipart/related;type="application/xop+xml". The client and service bindings may be mismatched.
Here is the web.config service model section
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="NewBehavior0" />
</endpointBehaviors>
</behaviors>
<services>
<service name="AvyProViewer.FileService">
<endpoint address="UploadFile" binding="basicHttpBinding" bindingConfiguration=""
contract="AvyProViewer.FileService" />
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
<bindings>
<basicHttpBinding>
<binding name="NewBinding0" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"
messageEncoding="Mtom" transferMode="StreamedRequest">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
</binding>
</basicHttpBinding>
</bindings>
Here is the service:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class FileService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "UploadFile")]
public string UploadFile(Stream fileStream)
{
string path = HostingEnvironment.MapPath("~");
string fileName = Guid.NewGuid().ToString() + ".jpg";
FileStream fileToupload = new FileStream(path + "\\FileUpload\\" + fileName, FileMode.Create);
byte[] bytearray = new byte[10000];
int bytesRead, totalBytesRead = 0;
do
{
bytesRead = fileStream.Read(bytearray, 0, bytearray.Length);
totalBytesRead += bytesRead;
} while (bytesRead > 0);
fileToupload.Write(bytearray, 0, bytearray.Length);
fileToupload.Close();
fileToupload.Dispose();
return fileName;
}
}
And here is where I expose it in my MVC3 routes:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(new ServiceRoute("FileService", new WebServiceHostFactory(), typeof(FileService)));
. . .
}
I think the issue is with the mtom declaration for messageEncoding in your binding. Try changing messageEncoding to Text.
Answer ended up being a combination of three different stack overflow posts. None by themselves solved the question but each provided crucial clues as to what was happing.
It seems that if you add a ServiceRoute the web.config binding information is ignored. This SO post clued me in to what seems to be undocumented behavior of this function: Unable to set maxReceivedMessageSize through web.config
I then used this post to determine how to programatically override the maxreceivedmesssagesize for the binding: Specifying a WCF binding when using ServiceRoute.
Unfortunately the code form #2 didn't work out of the box (not sure if the binding behavior for ServiceRoute has changed or what makes the difference). Turns out that if you specify a ServiceRoute its automatically created as a CustomBinding which can't be cast to the WebHTTPBinding type used in #2. So this post: How to set the MaxReceivedMessageSize programatically when using a WCF Client? helped me determine how to change the code in #2 to add this capability to a custom binding.

Edit System.Servicemodel values programmatically?

When using WCF, there is a section in the web.config as below.
<system.serviceModel>
<services>
<service name="abc">
<endpoint /> <---this
</service>
</services>
</system.serviceModel>
Is it possible to edit the area I've marked programmatically?
I can see there is a sytem.serviceModel namespace, but other than that I'm a bit lost.
If you want to change these parameters at runtime you can override ServiceHost.OnOpening()
E.g. to change port:
protected override void OnOpening()
{
foreach (ServiceEndpoint endpoint in Description.Endpoints)
{
string uriString = string.Format("{0}://{1}:{2}{3}",
endpoint.Address.Uri.Scheme,
endpoint.Address.Uri.Host,
endpoint.Address.Uri.Port + _basePort,
endpoint.Address.Uri.LocalPath);
endpoint.Address = new EndpointAddress(uriString);
}
base.OnOpening();
}
To complement Mike Mozhaev's answer, since your service is hosted in IIS you'll need a ServiceHostFactory to get a reference to the service host (or to use your own host). There's some information about it at http://blogs.msdn.com/b/carlosfigueira/archive/2011/06/14/wcf-extensibility-servicehostfactory.aspx.

How do I set bindingNamespace when using fileless activation?

I'm attempting to eliminate tempuri.org from my WCF service, hosted in IIS using fileless activation. I've followed the instructions here, and I'm stuck when it comes to the bindingNamespace attribute in Web.config, because I'm using fileless activation.
My Web.config merely contains:
<serviceActivations>
<add relativeAddress="Foo.svc"
service="BigCorp.Services.Foo, BigCorp.Services"
/>
</serviceActivations>
I therefore don't have an <endpoint> node on which to set bindingNamespace.
What to do?
You can still use the <services> and hence <endpoint> nodes with WCF File-less activation. Take a look at the following example, where I even modify the default wsHttpBinding to add transport security and enable default behaviors as well; all for the file-less activation of the "Module1.DES.ExternalDataService" service.
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding messageEncoding="Mtom">
<security mode="Transport"/>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Module1.DES.ExternalDataService">
<endpoint binding="wsHttpBinding" bindingNamespace="" contract="Module1.DES.IExternalDataService"/>
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
<serviceActivations>
<add relativeAddress="ExternalDataService.svc" service="Module1.DES.ExternalDataService"/>
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>
Hope this helps.
To change the binding namespace you can use a custom factory (instead of the default one provided) where you can change all the properties of the binding:
<serviceActivations>
<add relativeAddress="Foo.svc"
service="BigCorp.Services.Foo, BigCorp.Services"
factory="BigCorp.Services.FooHostFactory, BigCorp.Services"/>
</serviceActivations>
And the factory:
public class FooHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new FooServiceHost(serviceType, baseAddresses);
}
}
public class FooServiceHost : ServiceHost
{
public FooServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses) { }
protected override void OnOpening()
{
base.OnOpening();
foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
{
if (!endpoint.IsSystemEndpoint)
{
endpoint.Binding.Namespace = "http://services.bigcorp.com/foo";
}
}
}
}
In your service code, you specify:
[ServiceContract(Namespace="http://your-url")]
public interface IMyInterface { ... }
and you can also specify it for data contracts:
[DataContract(Namespace="http://your-url/data")]
public class MyData { ... }
Besides the obvious change of service/data contract namespaces, you can also set a namespace on the Binding object itself, as well as a namespace on the service description:
Binding binding = new BasicHttpBinding();
binding.Namespace = "urn:binding_ns";
ServiceHost host = new ServiceHost(typeof(MyService), address);
var endpoint = host.AddServiceEndpoint(typeof(IMyService), binding, "");
host.Description.Namespace = "urn:desc_ns";
The latter one is what controls the targetNamespace of the WSDL document itself.
In the end, I used a custom BindingNamespaceAttribute, derived from this example.
If you are using the fileless service activation feature of WCF 4.0 via the serviceActivations config element, then you can override the AddDefaultEndpoints base method in your ServiceHost implementation.
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace MyApp.WS.WCFServiceHost
{
public class MyHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new EDOServiceHost(serviceType, baseAddresses);
}
}
public class MyServiceHost : ServiceHost
{
public EDOServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses) { }
public override System.Collections.ObjectModel.ReadOnlyCollection<ServiceEndpoint> AddDefaultEndpoints()
{
var endpoints = base.AddDefaultEndpoints();
foreach (ServiceEndpoint endpoint in endpoints)
{
if (!endpoint.IsSystemEndpoint)
{
endpoint.Binding.Namespace = NamespaceConstants.MyNamespace;
}
}
return endpoints;
}
}
}
Or you could just use config only, the only down side to that is you violate the DRY principle slightly since you now have two points to maintain the namespace string, one in your constants and one in the config file.
In the following example im using the WCFExtrasPlus behaviour to 'flatten' the WSDL. You don't need this if your deploying to a .net 4.5 IIS7 server as you will have access to a flat WSDL anyway which is a new feature built into the 4.5 framework, I digress.
The example also assumes two service contracts and two service behaviour implementations of those contracts.
<system.serviceModel>
<services>
<service name ="MyApp.WS.ServiceBehaviour.Enquiries">
<endpoint bindingNamespace="MyApp.WS" binding="basicHttpBinding" contract="MyApp.WS.ServiceContract.IEnquiries" />
</service>
<service name ="MyApp.WS.ServiceBehaviour.CallLogging">
<endpoint bindingNamespace="MyApp.WS" binding="basicHttpBinding" contract="MyApp.WS.ServiceContract.ICallLogging" />
</service>
</services>
<serviceHostingEnvironment>
<serviceActivations>
<add relativeAddress="Enquiries.svc"
service="MyApp.WS.ServiceBehaviour.Enquiries"
/>
<add relativeAddress="CallLogging.svc"
service="MyApp.WS.ServiceBehaviour.CallLogging"
/>
</serviceActivations>
</serviceHostingEnvironment>
<extensions>
<behaviorExtensions> <!-- The namespace on the service behaviour, the service contract, the data contract and the binding must all be set to the same.-->
<add name="wsdlExtensions" type="WCFExtrasPlus.Wsdl.WsdlExtensionsConfig, WCFExtrasPlus, Version=2.3.1.8201, Culture=neutral, PublicKeyToken=f8633fc5451b43fc" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior>
<wsdlExtensions singleFile="true" />
</behavior>
</endpointBehaviors>
<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 faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
For reference the service contracts;
[ServiceBehavior(Namespace = NamespaceConstants.MyNamespace)]
public class CallLogging : ICallLogging
{
}
[ServiceBehavior(Namespace = NamespaceConstants.MyNamespace)]
public class Enquiries : IEnquiries
{
}
NB: A namespace does not need http:// in its name. It can be the namespace of your project if you like i.e. MyApp.MyProject.Somthing . See URN