Subscribing to TFS events and WCF - wcf

Sorry for asking a question about something I don't know much about, but I've been pulling my hair out trying to get this working.
So, I have a WCF service that is hosted on IIS and seems to be working insomuch that I can "see" it on the network by going to http://servername/MyService.svc in a browser.
That .svc looks like:
<% #ServiceHost Service="Foo.Bar" %>
The relevant code looks like:
[ServiceContract(Namespace = "http://schemas.microsoft.com/TeamFoundation/2005/06Services/Notification/03")]
public interface IBar
{
[OperationContract(Action = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify", ReplyAction = "*")]
[XmlSerializerFormat(Style = OperationFormatStyle.Document)]
void Notify(string eventXml, string tfsIdentityXml);
}
and:
public class Bar : IBar
{
public void Notify(string eventXml, string tfsIdentityXml)
{
// Just some test output to see if it worked
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "tfs.txt");
File.WriteAllText(path, tfsIdentityXml + eventXml);
}
}
That's all been built and the ensuing .dll put into the bin dir in the site root in IIS.
I now want to subscribe via bissubscribe.exe (or a similar method) to TFS check-in events. I tried doing something like:
bissubscribe /eventType CheckinEvent
/address http://servername/MyService.svc
/deliveryType Soap
/server mytfsserver
But nothing; it doesn't even look like there was log activity. So keeping in mind I know nothing about WCF, what am I doing wrong? I imagine the address param is one thing; am I not supposed to point it to the .svc?

I have created a blog post how you can use WCF in combination with the Event Services of TFS: http://www.ewaldhofman.nl/post/2010/08/02/How-to-use-WCF-to-subscribe-to-the-TFS-2010-Event-Service-rolling-up-hours.aspx

TFS 2010 and WCF 4.0 configurations are described below...
Method signature:
public void Notify(string eventXml) /* No SubscriptionInfo! */
Web config:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="Microsoft.TeamFoundation, Version=10.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
</compilation>
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="NotificationServiceBehavior" name="TF.CheckinListener.CheckinListener">
<endpoint address="Notify" binding="wsHttpBinding" bindingConfiguration="noSecurity" contract="TF.CheckinListener.ICheckinListener" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="NotificationServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
</binding>
<wsHttpBinding>
<binding name="noSecurity" maxBufferPoolSize="20000000" maxReceivedMessageSize="200000000">
<readerQuotas maxStringContentLength="200000000" maxArrayLength="200000000" />
<security mode="None" />
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Subscription address for bissubscribe:
http://MachineName/VirtualDirectoryName/Service.svc/Notify

One point that jumps out is the fact you have a method that doesn't return anything except void. Those should be marked as "one-way" method in WCF:
[ServiceContract(Namespace = "http://schemas.microsoft.com/TeamFoundation/2005/06Services/Notification/03")]
public interface IBar
{
[OperationContract(Action = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify", ReplyAction = "*", IsOneWay=true)]
[XmlSerializerFormat(Style = OperationFormatStyle.Document)]
void Notify(string eventXml, string tfsIdentityXml);
}
Add the "IsOneWay=true" to your [OperationContract] attribute.
Other than that, there's nothing obviously wrong in your code, but to really tell, we'd need a lot more config info to really tell. Try the IsOneWay=true first and see if that solves your issue.

How is your service configured? In particular, is it configured to use basicHttpBinding?
Try creating a client to call your service to make sure it can be called.
Then, see if there's an example service from the TFS SDK - see if you can get the example to work.

I was able to complete this connection with the following:
[ServiceContract(Namespace = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03")]
public interface ITeamSystemObserver : IObservable
{
[OperationContract( Action = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify", ReplyAction = "*" )]
[XmlSerializerFormat(Style=OperationFormatStyle.Document)]
void Notify(string eventXml, string tfsIdentityXml, SubscriptionInfo SubscriptionInfo);
}
Note you are missing the SubscriptionInfo parameter. Here is my web.config:
<basicHttpBinding>
<binding name="TfsEventServiceBasic">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" />
</security>
</binding>
</basicHttpBinding>

Related

Does Visual Studio Online support SOAP WorkItem change notification?

The reason I ask, is that I've set up a WCF service following the guidance out there (specifics below), and set up a SOAP notification in Visual Studio Online and my service doesn't appear to be called. IIS 8.5 Logs show no attempt to make contact with the service from VSO servers.
In case it IS supported, here are relevant bits to see if I have something set up wrong on the service side.
WCF Service - .NET 4.5.1 hosted as an Azure WebRole
Contract and Implementation
[ServiceContract(Namespace = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03")]
public interface IWorkItemSubscriber
{
[OperationContract(Action = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify")]
[XmlSerializerFormat(Style = OperationFormatStyle.Document)]
void Notify(string eventXml, string tfsIdentityXml);
}
// Note, I've tried w/ and w/out this Compatibility Attribute
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class WorkItemSubscriber : IWorkItemSubscriber
{
public void Notify(string eventXml, string tfsIdentityXml)
{
// Do stuff
}
}
Web.Config
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5.1" />
<customErrors mode="Off"/>
</system.web>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="noSecurity">
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="VsoNotificationService.Wcf.WorkItemSubscriber" behaviorConfiguration="eventServiceBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="noSecurity" contract="VsoNotificationService.Wcf.IWorkItemSubscriber" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="eventServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" aspNetCompatibilityEnabled="true" /> <!-- note: I've tried w/ and w/out the aspNetCompatibilityEnabled attribute -->
</system.webServer>
</configuration>
Visual Studio Online Configuration
I've confirmed I can hit the service, via creating my own client and calling the service. Whatever code I put in the method is executed, and if I do remote debugging via Visual Studio 2013 I'll hit the method's breakpoint. So the service is up and running, I just don't see traffic from Visual Studio Online (via code breakpoint, code content, nor IIS Logs). Makes me think that feature is not working there?
I see that you are missing the aspNetCompatibilityEnabled="true" on the <serviceHostingEnvironment element, as compared to my working solution, it might be the missing piece of the puzzle?
I only also achieved success by carefully including all little config settings.

Setting binding in WCF service

This may seem like a really easy question but I can't seem to figure it out at all.
I'm trying to create a new WCF service, and I'm new to having to secure them. I'm using a custom username/password for authentication. The problem [right now anyways] that I seem to be running into is that I can't figure out how to define the service to use the WSHttpBinding (on the service side, not the client side).
Am I missing something incredibly simple? Any pointers and/or recommendations would be greatly appreciated!
EDIT
Here's my code so far:
IAccountService
[ServiceContract]
public interface IAccountService
{
[OperationContract]
bool IsCardValid(string cardNumber);
[OperationContract]
bool IsAccountActive(string cardNumber);
[OperationContract]
int GetPointBalance(string cardNumber);
}
Service web.config
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false 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="false"/>
<StructureMapServiceBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="StructureMapServiceBehavior" type="Marcus.Loyalty.WebServices.Setup.StructureMapServiceBehavior, Marcus.Loyalty.WebServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
<service name="Marcus.Loyalty.WebServices.Account.IAccountService">
<endpoint address=""
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_Config"
contract="Marcus.Loyalty.WebServices.Account.IAccountService"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_Config"/>
</wsHttpBinding>
</bindings>
</system.serviceModel>
Testing app (console app)
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please enter card number");
var number = Console.ReadLine();
var endPoint = new EndpointAddress("http://localhost:59492/Account/AccountService.svc");
var binding = new WSHttpBinding(SecurityMode.Message);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
var cf = new ChannelFactory<IAccountService>(binding, endPoint);
cf.Credentials.UserName.UserName = "testuser";
cf.Credentials.UserName.Password = "Password1!";
var service = cf.CreateChannel();
var balance = service.IsAccountActive(number);
Console.WriteLine("\nBALANCE: {0:#,#}", balance);
Console.Write("\n\nPress Enter to continue");
Console.Read();
}
}
Testing app app.config
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="BasicHttpBinding_IAccountService" />
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:59492/Account/AccountService.svc"
binding="wsHttpBinding" bindingConfiguration="BasicHttpBinding_IAccountService"
contract="ServiceReference1.IAccountService" name="BasicHttpBinding_IAccountService" />
</client>
</system.serviceModel>
</configuration>
You need to define the abc (address, binding, contract) configuration into de web.config file (you can also do it programmatically. the b part, the binding, you can specify the wsHttpBinding
<system.serviceModel>
<services>
<service name = "MyNamespace.MyService">
<endpoint
address = "http://localhost:8000/MyService"
binding = "wsHttpBinding"
contract = "MyNamespace.IMyContract" />
</service>
</services>
</system.serviceModel>
If you wish to enable security in a proper way, there is a lot of literature and options. You can use certificates, windows based, tokens, ... passing a username & password like a parameter could not be the best way to do it.
There is an extensive sample on MSDN (How to: Specify a Service Binding in code) - but basically, you need to have:
your service contract (IMyService)
an implementation of that service (MyService)
a code where you create your ServiceHost to host your service
You got all of that? Great!
In that case, just do something like this:
// Specify a base address for the service
string baseAddress = "http://YourServer/MyService";
// Create the binding to be used by the service.
WsHttpBinding binding1 = new WsHttpBinding();
using(ServiceHost host = new ServiceHost(typeof(MyService)))
{
host.AddServiceEndpoint(typeof(IMyService), binding1, baseAddress);
host.Open();
Console.ReadLine();
}
and now you should have your service host up and running, on your chosen base address and with the wsHttpBinding defined in code.

Self hosting WCF contract not found

I used this MSDN example to construct my console host app:
http://msdn.microsoft.com/en-us/library/ms731758.aspx
It works I have a service running, while running I can add it as a Service Reference to my Silverlight class library ViewModel and I can see it running in a browser.
However when I run my Silverlight app I get the following error message:
Could not find default endpoint element that references contract
'ServiceLayer.IServiceLayer' in the ServiceModel client configuration
section. This might be because no configuration file was found for
your application, or because no endpoint element matching this
contract could be found in the client element.
This is my service code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Activation;
using Model;
namespace ServiceLayer
{
[ServiceContract(Namespace = "ServiceLayer")] //This has to match references or updating the service reference will not work.
public interface IServiceLayer
{
[OperationContract] //This is needed for each method.
string Compile(string cscode, string name, string type, int token);
[OperationContract] //This is needed for each method.
string LoginClick(string username, string password, bool createuser, int token);
[OperationContract]
bool IsLoggedIn(int token);
[OperationContract] //This is needed for each method.
object[] GetMessages(int token);
[OperationContract] //This is needed for each method.
int GetToken(int token);
[OperationContract] //This is needed for each method.
string[][] GetPlugins(int token);
[OperationContract] //This is needed for each method.
string Finalize(int token, string[] descriptions);
[OperationContract] //This is needed for each method.
object[][] Communicate(string methodName, int token, object[] args);
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ServiceLayer : IServiceLayer
{
public string Compile(string cscode, string name, string type, int token)
{
// Add your operation implementation here
ModelInterface MI = new ModelInterface();
return MI.Compile(cscode, name, type, token);
}
public String LoginClick(string username, string password, bool createuser, int token)
{
ModelInterface MI = new ModelInterface();
return MI.LoginClick(username, password, createuser, token);
}
public bool IsLoggedIn(int token)
{
ModelInterface MI = new ModelInterface();
return MI.IsLoggedIn(token);
}
public object[] GetMessages(int token)
{
ModelInterface MI = new ModelInterface();
return MI.GetMessages(token);
}
public int GetToken(int token)
{
ModelInterface MI = new ModelInterface();
return MI.GetToken(token);
}
public string[][] GetPlugins(int token)
{
ModelInterface MI = new ModelInterface();
return MI.GetPlugins(token);
}
public string Finalize(int token, string[] descriptions)
{
ModelInterface MI = new ModelInterface();
return MI.Finalize(token, descriptions);
}
public object[][] Communicate(string methodName, int token, object[] args)
{
ModelInterface MI = new ModelInterface();
return MI.Communicate(methodName, token, args);
}
// Add more operations here and mark them with [OperationContract]
}
}
This is the ServiceReferences.ClientConfig of the ViewModel:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IServiceLayer" maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
<binding name="BasicHttpBinding_IServiceLayer1" maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:3263/front" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IServiceLayer" contract="IServiceLayer"
name="BasicHttpBinding_IServiceLayer" />
<endpoint address="http://localhost:3263/front" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IServiceLayer1" contract="ServiceLayer.IServiceLayer"
name="BasicHttpBinding_IServiceLayer1" />
</client>
</system.serviceModel>
And this is the Web.Config file of the project with the website hosting the Silverlight app.
I post it because I tried this Could not find default endpoint element
guide - ie copying the servicemodel section from the VM config to the web.Config below also:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<pages controlRenderingCompatibilityVersion="4.0"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="OrgOS.Web.ServerCommunicationService.customBinding0">
<binaryMessageEncoding/>
<httpTransport/>
</binding>
<binding name="OrgOS.Web.ServiceLayer.customBinding0">
<binaryMessageEncoding/>
<httpTransport/>
</binding>
</customBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
<services>
<service name="OrgOS.Web.ServiceLayer">
<endpoint address="" binding="customBinding" bindingConfiguration="OrgOS.Web.ServiceLayer.customBinding0" contract="OrgOS.Web.ServiceLayer"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
My server I rented came without ASP.NET despite the fact I paid for it, support is on leave and I have deadlines coming up!
Trying to get WCF working is driving me mad - please help me...
Why is it so freaking hard to show a simple Silverlight project...
Ah nevermind; you have to post the config settings in both the View Silverlight project, the web project AND the ViewModel Silverlight project.
You must also delete all endpoints and bindings, but one to avoid getting things confused.
Also changed things to make sure namespace="namespace" fits the actual namespace.
... not that things work for that reason, oh noes all I get is invalid results.
FML right now.

Failed to Execute URL when calling a WCF service with Windows authentication

I am having a problem with a WCF service using Windows authentication on one of the servers I am deploying it to (it's a Windows Server 2008 R2 machine), while it works flawlessly on all other machines I have access to (Windows 7, Windows Server 2008 and Windows Server 2008 R2). I managed to reproduce the issue with a really simple sample application which more or less completely excludes my code as the cause of the problem.
The minimum application I can reproduce the issue with is a minor modification of the WCF service project template:
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
}
[AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}\nUsername: {1}",
value,
ServiceSecurityContext.Current == null ?
"<null>" :
ServiceSecurityContext.Current.PrimaryIdentity.Name);
}
}
Basically I enabled ASP.NET compatibility (I need it because the actual code uses an HttpHandler for authentication) and the username of the authenticated user is returned.
The contents of web.config are as follows:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<authentication mode="Windows"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="HttpWindowsBinding" maxReceivedMessageSize="2147483647">
<readerQuotas maxBytesPerRead="2147483647" maxArrayLength="2147483647" maxStringContentLength="2147483647" maxNameTableCharCount="2147483647" maxDepth="2147483647"/>
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
<service name="TestService.Service1" behaviorConfiguration="ServiceBehavior">
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="HttpWindowsBinding"
contract="TestService.IService1" />
<endpoint address="problem"
binding="basicHttpBinding"
bindingConfiguration="HttpWindowsBinding"
contract="TestService.IService1" />
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Notice the two endpoints: one with the default address, the other one with a relative address. Calling the first one succeeds even on the problematic server, while the call to the second one fails with following error:
Exception type: HttpException
Exception message: Failed to Execute URL.
at System.Web.Hosting.ISAPIWorkerRequestInProcForIIS6.BeginExecuteUrl(String url, String method, String childHeaders, Boolean sendHeaders, Boolean addUserIndo, IntPtr token, String name, String authType, Byte[] entity, AsyncCallback cb, Object state)
at System.Web.HttpResponse.BeginExecuteUrlForEntireResponse(String pathOverride, NameValueCollection requestHeaders, AsyncCallback cb, Object state)
at System.Web.DefaultHttpHandler.BeginProcessRequest(HttpContext context, AsyncCallback callback, Object state)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
The call only fails when the classic pipeline is used (I need it because of the HttpHandler, but the issue can be reproduced even without it). With integrated pipeline the problem is gone. Also if I disable Windows authentication, the problem is gone as well:
<binding name="HttpBinding" maxReceivedMessageSize="2147483647">
<readerQuotas maxBytesPerRead="2147483647" maxArrayLength="2147483647" maxStringContentLength="2147483647" maxNameTableCharCount="2147483647" maxDepth="2147483647"/>
<security mode="None">
<transport clientCredentialType="None" />
</security>
</binding>
I have noticed another detail with an HttpHandler registered. The value of HttpRequest.CurrentExecutionFilePath property for the endpoint with the relative address differs between the problematic server (~/Service1.svc/problem) and the working servers (~/Service1.svc). Although I'm not that well familiar with IIS, I suspect this could hint at the cause of the problem - maybe something related to the routing of requests?
I am running out of ideas therefore I'm posting this here in the hope the someone will recognize what the problem could be. Any suggestion are welcome.
Do you have URL rewriting on IIS turned on? This smells like a permission issue of some sort. What is the difference between Classic and Integrated pipeline mode in IIS7? Might be helpful.
The problem may be the address "~/Service1.svc/problem"
When the address is "~/Service1.svc" the call hits the svc file, and uses the information in the file to find the interface and then the configuration for that interface.
When you use a relative address without a svc file, it looks at the address in the config file.
Do you have a directory "Service1.svc" on one of the servers, or is the address without the ".svc" on the server where it works?

Enabling WCF Help page changes response from JSON to XML

I'm working on creating a WCF web service that communicates via JSON. I got the service to a point that it's working and I'm trying to set up the help page so the developers that will consume the service can have some documentation to work by.
The issue that I'm running into is that when I did get the help page up and running, all the responses being sent out by my service changed from JSON to XML.
I'll be the first to admit that I'm very new to this. There might be some fundamental flaw with how I've structured my service, or it might be as simple as a flag I missed in the web.config... I'm really at a loss at this point.
What I found, through basically just trial and error and beating my head against the wall, was if I change the name attribute of the following line in the Web.config:
<standardEndpoint name="serviceEndpoint" helpEnabled="true" automaticFormatSelectionEnabled="true">
To be empty string:
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true">
The help page magically shows up, but my services are now spitting out XML instead of JSON.
I think it's probably better to over-share than to under-share for something as specific as this, so here's what I think is the relevant bits of the set-up. I apologize for the mono-tone code, I can edit it to be more readable if I figure out how.
Service Interface:
[OperationContract]
[Description("DESCRIPTIONATION HAPPENS")]
[WebInvoke(Method = "GET",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "GetYears")]
GetYearsReply GetYears();
...
Service Implementation:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MPG : IMPG
{
public GetYearsReply GetYears()
{
GetYearsReply reply = new GetYearsReply();
reply.YearList = generateYears();
return reply;
}
...
Global.asax:
<%# Application Codebehind="Global.asax.cs" Inherits="MPG_Service.Global" Language="C#" %>
Global.asax.cs:
namespace MPG_Service
{
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("garage", new WebServiceHostFactory(), typeof(MPG)));
}
}
}
Web.config:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</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>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<standardEndpoints>
<webHttpEndpoint>
<!--
Configure the WCF REST service base address via the global.asax.cs file and the default endpoint
via the attributes on the <standardEndpoint> element below
-->
<standardEndpoint name="serviceEndpoint" helpEnabled="true" automaticFormatSelectionEnabled="true">
<!--<security mode="Transport">
<transport clientCredentialType="None"/>
</security>-->
</standardEndpoint>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
</configuration>
If anyone has any insight into why this behavior is happening, or any other major screw-ups in my code I'd love any input.
Your client is saying that it accepts XML (application/xml), so that's what WCF is returning. That is consistent with the Automatic Formatting rules (see details at http://msdn.microsoft.com/en-us/library/ee476510.aspx). If you don't want that behavior, then set autoFormatSelectionEnabled to false in your configuration.