how to make WCF webHttp behaviour accept HEAD verbs? - wcf

I have a WCF service hosted in a Windows service.
I've added to it a webHttpBinding with a webHttp behaviour and whenever I send it a GET request I get http 200 which is what I want, problem is I get an http 405 whenever I send it a HEAD request.
Is there a way to make it return http 200 also for HEAD?
Is that even possible?
edit: that's the operation contract:
[OperationContract]
[WebGet(UriTemplate = "MyUri")]
Stream MyContract();

[ServiceContract]
public interface IService
{
[OperationContract]
[WebGet(UriTemplate="/data")]
string GetData();
}
public class Service : IService
{
#region IService Members
public string GetData()
{
return "Hello";
}
#endregion
}
public class Program
{
static void Main(string[] args)
{
WebHttpBinding binding = new WebHttpBinding();
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri("http://localhost:9876/MyService"));
host.AddServiceEndpoint(typeof(IService), binding, "http://localhost:9876/MyService");
host.Open();
Console.Read();
}
}
The above code works fine. I get a 405 (Method not allowed) on HEAD request. The version of assembly I am using is System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35.
Actually as far as I know there is no straight forward way of allowing it.However you could try something like the solution below..But this has to be done for each method that needs GET and HEAD, which makes it a not so elegant solution..
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method = "*", UriTemplate = "/data")]
string GetData();
}
public class Service : IService
{
#region IService Members
public string GetData()
{
HttpRequestMessageProperty request =
System.ServiceModel.OperationContext.Current.IncomingMessageProperties["httpRequest"] as HttpRequestMessageProperty;
if (request != null)
{
if (request.Method != "GET" || request.Method != "HEAD")
{
//Return a 405 here.
}
}
return "Hello";
}
#endregion
}

Sounds like a serious bug in the service (or even the framework). Support for HEAD in HTTP/1.1 is in no way optional.

Related

WCF avoiding too many endpoints for experts

I have a lot of businesses services already implemented, and I´m exposing them as services by WCF.
I don´t like the idea to have one endpoint to each service..... it could be a problem to maintain in the future as my repository grows.......
I´d like to know wcf´s experts opinions if the code below would be a good approach an them I can move ahead with this solution.
Business Service A:
[ServiceContract]
public interface IServiceA
{
[OperationContract]
object AddA(object a);
[OperationContract]
object Update();
}
Business Service B:
[ServiceContract]
public interface IServiceB
{
[OperationContract]
object AddB(object b);
[OperationContract]
object Update();
}
Concrete implementation for Service A
public class ConcreteServiceA : IServiceA
{
public object AddA(object a)
{
Console.WriteLine("ConcreateServiceA::AddA");
return null;
}
public object Update()
{
Console.WriteLine("ConcreateServiceA::Update");
return null;
}
}
Concrete implementation for Service B
public class ConcreteServiceB : IServiceB
{
public object AddB(object b)
{
Console.WriteLine("ConcreateServiceB::AddB");
return null;
}
public object Update()
{
Console.WriteLine("ConcreateServiceB::Update");
return null;
}
}
My single service is partial to separate concerns to each service.
Note that it´s constructors depends on both business services above, will be injection using IoC
Partial for constructors
public partial class WCFService
{
IServiceA _a;
IServiceB _b;
public WCFService()
: this(new ConcreteServiceA(), new ConcreteServiceB())
{
}
public WCFService(IServiceA serviceA, IServiceB serviceB)
{
_a = serviceA;
_b = serviceB;
}
}
Partial class implementing only IServiveA
public partial class WCFService : IServiceA
{
object IServiceB.AddB(object b)
{
return _b.AddB(b);
}
object IServiceB.Update()
{
return _b.Update();
}
}
Partial class implementing only IServiceB
public partial class WCFService : IServiceB
{
object IServiceA.AddA(object a)
{
return _a.AddA(a);
}
object IServiceA.Update()
{
return _a.Update();
}
}
And in the client side, I using like that:
var endPoint = new EndpointAddress("http://localhost/teste");
ChannelFactory<IServiceA> _factoryA = new ChannelFactory<IServiceA>(new BasicHttpBinding(), endPoint);
IServiceA serviceA = _factoryA.CreateChannel();
serviceA.Update();
var netTcpEndPoint = new EndpointAddress("net.tcp://localhost:9000/teste");
ChannelFactory<IServiceB> _factoryB = new ChannelFactory<IServiceB>(new NetTcpBinding(), netTcpEndPoint);
IServiceB serviceB = _factoryB.CreateChannel();
serviceB.Update();
I really appreciate any opinion or other suggestions.
There's nothing wrong with multiple endpoints - it's part of the process. What is wrong, however, is duplicating functionality over multiple endpoints. How many "UpdateThis's" or "AddThat's" developers need? This can get out of control and makes for a maintenance headache. Just look at your constructor, it will grow and grow as you add new services and consolidate them into one service.
Think coarse-grained not fine-grained.
As an alternative, maybe you can try passing request objects as a parameter and returning response objects. This approach may streamline your code and help you avoid the maintenance issues you mention in your post and gives you a suggestion.
So, it looks something like this:
// Your service will return a very generic Response object
public interface IService
{
Response YourRequest(Request request);
}
// Your service implementation
public partial class WCFService : IService
{
Response IService.YourRequest(Request request)
{
//inspect the Request, do your work based on the values
//and return a response object
}
}
// Your request object
public class Request()
{
object YourClass{get;set;}
DoWhat Action{get;set;} //enum, constants, string etc.
int ID {get; set;}
}
// Your response object
public class Response()
{
bool Success {get; set;}
}
// Create Request object
var request = new Request(){YourClass = YourClassName , Action DoWhat.Update(), ID=1};
// Your service call
var endPoint = new EndpointAddress("http://localhost/teste");
ChannelFactory<IService> _factory = new ChannelFactory<IService>(new BasicHttpBinding(), endPoint);
IService service = _factory.CreateChannel();
var response = service.YourRequest(request);
So, now you've removed the fine-grained approach and replaced it with course-grained one. Let me know if you'd like more detail.

In WCF how do I remove the 404 response body?

I have a WCF service configured and I'm using routing to configure it. Everything is working the way I want it, except the 404 messages have a body stating Service Endpoint not found.
I'd like the 404 to have an empty response body.
Here is my route registration:
public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
private void RegisterRoutes(RouteCollection routes)
{
routes.Add(new ServiceRoute("RootService", new WebServiceHostFactory(), typeof(ServiceProvider)));
}
Here is my service class:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceContract]
public class ServiceProvider
{
[WebGet]
public Test ValidUrl()
{
return new Test();
}
}
How do I make the response for this url http://localhost/RootService have an empty 404 body?
I found a few ways to do this and I've listed two below. They key is having the UriTemplate set as *. This makes the method match all routes that aren't explicitly matched otherwise.
[WebGet(UriTemplate="*")]
public void ErrorForGet()
{
throw new WebFaultException(HttpStatusCode.NotFound);
}
I don't like this way as well, but it works:
[WebGet(UriTemplate="*")]
public void ErrorForGet()
{
WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
}
Both of these methods have overloads that take a string as a message to provide to the requesting client. The WebFaultException needs to be like this going that route though: throw new WebFaultException<string>("Resource not found", HttpStatusCode.NotFound);

Exposing a WCF Service REST-fully

I have created a brand new WCF Service. I created this service by just saying Add New Item... -> WCF Service in Visual Studio. I then edited the contract slightly to look like the following:
[ServiceContract]
public interface IMyService
{
[OperationContract]
[WebGet(UriTemplate = "/Authenticate/{username}/{password}", ResponseFormat = WebMessageFormat.Json)]
bool Authenticate(string username, string password);
}
My operations looks like the following:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(IncludeExceptionDetailInFaults = false)]
public class MyService : IMyService
{
public bool Authenticate(string username, string password)
{
try
{
return false;
}
catch (Exception ex)
{
throw new ApplicationException("Unknown exception");
}
}
}
When I visit: http://localhost:80/MyService.svc/Authenticate/someUserName/somePassword in my browser window, an empty screen appears. I was expecting "false" to appear in JSON syntax. What am I doing wrong?
Thank you!
Use a tool like Fiddler to see the actual HTTP messages. Helps with debugging.
Second, you're request URL is wrong. Try this:
http://localhost:80/MyService.svc/Authenticate/someUserName/somePassword
You do have a SVC file, correct? You'll need that if you're hosting this in IIS. If you're self hosting it in a WebServiceHost object, then you don't need them.
using( WebServiceHost host = new WebServiceHost( typeof( MyService) ) )
{
host.Open();
Console.WriteLine( "Service is running" );
Console.WriteLine( "Press enter to quit..." );
Console.ReadLine();
host.Close();
}

How to call a service operation at a REST style WCF endpoint uri?

is it possible to call a service operation at a wcf endpoint uri with a self hosted service?
I want to call some default service operation when the client enters the endpoint uri of the service.
In the following sample these uris correctly call the declared operations (SayHello, SayHi):
- http://localhost:4711/clerk/hello
- http://localhost:4711/clerk/hi
But the uri
- http://localhost:4711/clerk
does not call the declared SayWelcome operation. Instead it leads to the well known 'Metadata publishing disabled' page. Enabling mex does not help, in this case the mex page is shown at the endpoint uri.
private void StartSampleServiceHost()
{
ServiceHost serviceHost = new ServiceHost(typeof(Clerk), new Uri( "http://localhost:4711/clerk/"));
ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint(typeof(IClerk), new WebHttpBinding(), "");
endpoint.Behaviors.Add(new WebHttpBehavior());
serviceHost.Open();
}
[ServiceContract]
public interface IClerk
{
[OperationContract, WebGet(UriTemplate = "")]
Stream SayWelcome();
[OperationContract, WebGet(UriTemplate = "/hello/")]
Stream SayHello();
[OperationContract, WebGet(UriTemplate = "/hi/")]
Stream SayHi();
}
public class Clerk : IClerk
{
public Stream SayWelcome() { return Say("welcome"); }
public Stream SayHello() { return Say("hello"); }
public Stream SayHi() { return Say("hi"); }
private Stream Say(string what)
{
string page = #"<html><body>" + what + "</body></html>";
return new MemoryStream(Encoding.UTF8.GetBytes(page));
}
}
Is there any way to disable the mex handling and to enable a declared operation instead?
Thanks in advance, Dieter
Did you try?
[OperationContract, WebGet(UriTemplate = "/")]
Stream SayWelcome();
UPDATE:
Not sure why it is not working for you, I have a self hosted WCF service with the following service contract:
[ServiceContract]
public interface IDiscoveryService {
[OperationContract]
[WebGet(BodyStyle=WebMessageBodyStyle.Bare, UriTemplate="")]
Stream GetDatasets();
The only difference I can see is that I use WebServiceHost instead of ServiceHost.

Importing ASMX Web Service metadata to WCF Endpoint

I am interested in impersonating well-known Web Services and Wcf Services for integration test purposes. To this end, I would like to capture service metadata, auto-generate service stubs, and host service stubs in a self-hosted environment.
Following this article here, I am able to obtain remote Wcf Service metadata and generate contracts. However, I am having some difficulty doing the same for remote Asmx Web Services.
I have a set of mickey-mouse solutions for vetting this out.
My Asmx solution contains a default "Hello World" web service, found below
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class SimpleAsmxService : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld () { return "Hello World"; }
}
My Wcf solution contains a default "Hello World" service, also found below
[ServiceContract]
public interface ISimpleWcfService
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
}
[DataContract]
public class CompositeType
{
[DataMember]
public bool BoolValue { get; set; }
[DataMember]
public string StringValue { get; set; }
}
public class SimpleWcfService : ISimpleWcfService
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
Finally, the little console-that-could looks like
class Program
{
public const string UrlWcf =
"http://localhost:8731/Design_Time_Addresses/SimpleWcfService/mex";
public const string UrlAsmx =
"http://localhost:1803/SimpleAsmxService.asmx?WSDL";
static void Main(string[] args)
{
EndpointAddress mexAddress = new EndpointAddress (UrlWcf);
MetadataExchangeClient mexClient =
new MetadataExchangeClient (mexAddress);
mexClient.ResolveMetadataReferences = true;
// NOTE: blows up if we use UrlAsmx
MetadataSet metaSet = mexClient.GetMetadata ();
WsdlImporter importer = new WsdlImporter (metaSet);
Collection<ContractDescription> contracts =
importer.ImportAllContracts();
}
}
It seems to me that I should be able to pull Wsdl from a well-known Asmx Web Service and generate contracts [and from contracts to code], but cannot seem to contort the preceding sample to do so. Any help would be much appreciated,
Thanks!
NOTE: the error generated when invoking MetadataSet metaSet = mexClient.GetMetadata(); above is a System.InvalidOperationException with message of
Metadata contains a reference that cannot be resolved : 'http://localhost:1803/SimpleAsmxService.asmx?WSDL'
With a System.InvalidOperationException inner exception with message of
<?xml version="1.0" encoding="utf-16"?>
<Fault xmlns="http://www.w3.org/2003/05/soap-envelope">
<Code>
<Value>Sender</Value>
</Code>
<Reason>
<Text xml:lang="en">
System.Web.Services.Protocols.SoapException: Unable to handle request without a valid action parameter. Please supply a valid soap action.
at System.Web.Services.Protocols.Soap12ServerProtocolHelper.RouteRequest()
at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)
</Text>
</Reason>
</Fault>
The way to get it to work with an ASMX web service is to specify the MetadataExchangeClientMode
...
MetadataExchangeClient mexClient =
new MetadataExchangeClient (new Uri(), MetadataExchangeClientMode.HttpGet);
...
using MetadataExchangeClientMode.HttpGet for your ASMX services
and MetadataExchangeClientMode.MetadataExchange for your WCF services.