WCF. Framework 4.5.1
Existing web service uses DataContractSerializer.
It now needs to provide a contract that takes XMLSerialized data from a third party as an input parameter and return a serialized object.
Apparently I should be able to decorate that contract with [XMLSerializerFormat].
But this breaks the published site.
i.e. You can't even access the site with a web browser to obtain the wsdl.
Is there some extra work needed in the Web.Config?
[OperationContract]
[XmlSerializerFormat]
[WebInvoke(UriTemplate = "", Method = "POST")]
ResponseMessage Update(RequestMessage instance);
The contract is sitting inside an interface with all of the existing contracts
The interface is decorated
[ServiceContract]
public interface IMyService
{
Thanks
Bob
Problem was that the contract was not specifically decorated with xml.
Apparently the default assumption is JSON. Working declaration:
[OperationContract]
[XmlSerializerFormat]
[WebInvoke(UriTemplate = "Update", Method = "POST",
ResponseFormat = WebMessageFormat.Xml,
RequestFormat = WebMessageFormat.Xml)]
ResponseMessage Update(RequestMessage instance);
The 'Answer' I put up has exposed some very strange behavior.
With the [XMLSerializer] decoration in place, other contracts can no longer deserialize integers. The integer property leaves the client as say 6 and is deserialized as 0. Comment out the decoration and normal behavior resumes.
I will post a separate question about this.
Related
I have a project that needs to integrate with legacy asmx services that contains business logic that I must use. I had problems making use of the channels so were allowed to add attributes to make the services rest.
Now here is the problem, I have tested all the services with post man locally and all calls work perfectly with rest as well as the old application still working. Hosting the web app on my local IIS also works. My problem is that when I publish to the server I have some services saying that [ScriptService] attribute is not there but they are there. Then other services works. What am I missing with IIS or publishing? Here is some Name changed code sections. Had to change due to NDA
//Working Service locally returns xml
[System.Web.Services.WebService(Namespace = "http://TheService.ServiceContracts/2006/09", Name = "MyWorkingService")]
[System.Web.Script.Services.ScriptService]
public class MyWorkingService : IMyWorkingService
{
[System.ServiceModel.Web.WebInvoke(Method = "POST",
RequestFormat = System.ServiceModel.Web.WebMessageFormat.Json,
ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Json,
UriTemplate = "/PostMethod",
BodyStyle = System.ServiceModel.Web.WebMessageBodyStyle.Wrapped)]
[System.Web.Services.WebMethod]
public string PostMethod(MyPostObjectClass item)
{
//Business logic
}
}
//Failing Service; locally returns json
[System.Web.Services.WebService(Namespace = "http://TheService.ServiceContracts/2006/09", Name = "MyFailingService")]
[System.Web.Script.Services.ScriptService]
public class MyFailingService : IMyFailingService
{
[System.ServiceModel.Web.WebInvoke(Method = "POST",
RequestFormat = System.ServiceModel.Web.WebMessageFormat.Json,
ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Json,
UriTemplate = "/GetDataFromObjectFilters",
BodyStyle = System.ServiceModel.Web.WebMessageBodyStyle.Wrapped)]
[System.Web.Services.WebMethod]
public MyResponseObject GetDataFromObjectFilters(ObjectFilterRequestClass item)
{
//Business logic
}
}
So I looked at my local IIS and the server IIS to try and figure out what is wrong. Matched up the handler mappings, ISAPI filters and the IIS features that is installed on my local to the server. Yet the server kept asking for the script service attribute even if it was on the class.
After a full weekend of no relax time, I ended up creating a new service project to add the services into and then started adding api controllers into the project to run alongside these services. Making use of the same name as the services to make changes less troublesome in the ui application.
We have a WCF service that we recently moved all of the operations over to be asynchronous. We rolled this out with a new service contract to cover those operations, but both the new contract's ServiceContract Name and the OperationContract Names are pointed to the old ServiceContract and OperationContract Names to maintain backwards compatibility.
Something like this:
[ServiceContract(Namespace= "", Name = nameof(IOldContract))]
public interface INewAsyncContract
{
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]
[OperationContract(Name = nameof(IOldContract.DoThing))]
Task<DoThingResponse> DoThingAsync(DoThingRequest doThingRequest);
}
Whereas the old contract looks like this:
[ServiceContract]
public interface IOldContract
{
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]
[OperationContract]
DoThingResponse DoThing(DoThingRequest doThingRequest);
}
Note: The old service contract is missing a namespace value. This is accurate in the codebase.
Now for the issue:
When we deploy this service with a ServiceContract Namespace in the new service contract, we receive this error when navigating to the service:
An ExceptionDetail, likely created by
IncludeExceptionDetailInFaults=true, whose value is:
System.InvalidOperationException: An exception was thrown in a call to
a WSDL export extension:
System.ServiceModel.Description.DataContractSerializerOperationBehavior
contract: IOldContract ----> System.Xml.Schema.XmlSchemaException: The
global element 'DoThingResponse' has already been declared.
And conversely, if we publish without the namespace, the service operates as expected, but a default namespace is provided (http://tempuri.org), which we don't want.
Why is the ServiceContract namespace interfering with the declation of DoThingResponse?
Edit to add DoThingResponse definition:
[DataContract(Namespace = "")]
public class DoThingResponse
{
[DataMember]
public obj ResponseObj { get; set; }
}
It seems that we have declared the DoThingResponse repeatedly. I would like to know the way you define the custom class and the project structure.
The namespace attribute and name attribute of the service contract are applied to the metadata document exposed by the service (WSDL document). The namespace attribute is used as the namespace of the porttype element in the WSDL document. The default value is http://tempuri.org.
The name attribute applies the name of the porttype element in the WSDL document.
These values are unique and cannot be repeated.
Unique values are used to mark the elements in the envelope in the SOAP envelope.
Feel free to let me know if there is anything I can help with.
I got xml looking like:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>1</item>
<item>2</item>
<item>3</item>
</items>
And a wcf service contract:
[ServiceContract(Namespace = "", Name = "MyService", SessionMode = SessionMode.NotAllowed)]
public interface IMyService
{
[OperationContract]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Bare)]
void DoWork(IEnumerable<int> items);
}
Service binding is basic http.
But when i try to post that xml to wcf method i'm getting error:
Unable to deserialize XML message with root name "items" and root namespace ""
How should wcf method look like to properly work with that xml?
Your service contract does not seem to be setup correctly.
I think you need to implement a "wrapper" class, which defines a type structure that matches your XML.
For example:
[XmlRoot("items")]
public class MyItems
{
[XmlElement("item")]
public List<int> Items { get; set; }
}
I just put together a quick test app and successfully verified the interface using your sample XML (via a soapUI REST client).
Regards,
I think you need to specify a root namespace for default xml deserialization. If this is not an option for you, you might need to change your service interface to accept a stream instead.
Here's more information on the subject: http://www.codeproject.com/Articles/35982/REST-WCF-and-Streams-Getting-Rid-of-those-Names-Sp
To actually answer your question, you could try the following:
<?xml version="1.0" encoding="UTF-8"?>
<items xmlns="http://schemas.datacontract.org/2004/07/"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<item>1</item>
<item>2</item>
<item>3</item>
</items>
Edit: That was not your question. So to actually answer your question:
[OperationContract]
[WebInvoke(BodyStyle =
WebMessageBodyStyle.Bare, Method = "POST", ResponseFormat = WebMessageFormat.Xml,)]
void DoWork(Stream data);
Another alternative could be a custom datacontract (signature: void DoWork(MyCustomDataContract data);) and custom deserialization, example here:
How to use Custom Serialization or Deserialization in WCF to force a new instance on every property of a datacontact ?
I have various interfaces (endpoints) in a WCF service host, each for a completely different concern. In a classic soapy web service, I'm able to define a base host address (e.g. http://myhost.com/) and map each interface to a relative URI (IServiceContract -> service/, IMaintenanceContract -> maintenance/) so I can call them by e.g. http://myhost.com/service/mymethod.
Now I'm taking my first steps towards a RESTful WCF service using JSON as message format for CRUD web requests and the only thing I see to address an operation is by using the UriTemplate field from WebInvoke (or WebGet) attribute. Unfortunately, it doesn't seem that I can put this on the interface, just on operation contract methods.
How can I map each interface to a different relative URI?
Yes, you'll put the base url on the [OperationContract] methods. This is OK though, because you can specify any base url you want. Here is a sample interface that gives you this control.
namespace MyHostApi
{
[ServiceContract]
public interface IMyHostApi
{
[OperationContract]
[WebGet(BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "WhateverYouWant/HelloWorld/{name}")]
string HelloWorld(string format, string name);
}
}
I have an interface similar to this:
[ServiceContract]
public interface IBaseService<T>
{
[OperationContract]
[WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
List<T> LoadById(string value);
[OperationContract]
[WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
List<T> Load(string field, string value);
}
These methods are going to be implemented in several services. An example of implementation would be like this:
[ServiceContract]
[ServiceKnownType(typeof(ObjectDTO))]
public interface IObjectService : IBaseService<ObjectDTO>
{
}
My question is, is it possible to setup RESTful services using this architecture using UriTemplates on the OperationContracts in the base service interface? I tried searching around, but didn't see anyone else attempting to setup their RESTful services this way.
This is purely my HO, but I strongly recommend you to try out OpenRasta instead of added-as-an-afterthought-to-a-RPC-based-framework REST support in WCF. This is surely possible in OpenRasta.
Inheritance with web services is something that I've always found very problematic to achieve, as such I strongly advise against it. Instead consider building your web services as stubs and then call into shared logic with similar structure to what you have done.
If you want a way to easily create web services that are configuration-free you should check out servicestack.net. It's an open source web service framework that lets your create REST-full JSON and XML webservices without any endpoint configuration, Service or Operation contracts - using only POCO DataContracts. Here is a live example showing the client and server code needed for creating simple REST web services called by Ajax and Silverlight clients:
Given the way that REST services are consumed I'm not sure that there's any point using generics. You lose all the benefits of strong typing as you switch to what is a dynamic transport.
JSON serialisation is dynamic and not type aware, so why do you need to strongly type your output?
[ServiceContract]
public interface IBaseService
{
[OperationContract]
[WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
List<object> LoadById(string value);
[OperationContract]
[WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
List<object> Load(string field, string value);
}
Will produce the same JSON output.
Given that I think your best bet is just to not use generics in REST.
WCF does not allow open generics in its service contracts. It does provide a KnownType and ServiceKnownType attributes that let you "inform" it of your polymorphisms. You can provide a static helper method to the ServiceKnownType attribute that return the collection of known types as well. I've used this to return the types that match my generic configuration.