I have a struggling time with WCF REST to parse request body.
My problem:
POST method https://{Service}/X/Y? Z= 0000000
Body Request: {"id":"id"}
My code:
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Bare,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "/X/{y}?Z={z}")
]
string GetRequest(string y, string z, Request request);
}
public class Service : IService
{
public string GetRequest(string y, string z, Request request)
{
//Do sth
}
[DataContract]
public class Request
{
[DataMember]
[JsonProperty("id")]
public String Id { get; set; }
}
The problem that I have is that y and z has data and they are correct BUT id in request is null. I expected to be "id".
I searched a lot in the internet and I found Stream solution which is not easily to follow in this case.
I am wondering if anyone has a clever idea to do this.
Take a look on this version
The Service
namespace ServiceTest
{
internal class Program
{
private static WebServiceHost _service;
private static void Main()
{
_service = new WebServiceHost(typeof (Service));
Console.WriteLine("The service has started");
_service.Open();
Console.ReadKey();
}
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public sealed class Service : IService
{
public string GetRequest(string y, string z, Request request)
{
Console.WriteLine("Y: {0}, Z: {1}, request: {2}", y, z, request);
return "Ok";
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, UriTemplate = "/X/{y}?Z={z}")]
string GetRequest(string y, string z, Request request);
}
[DataContract]
public sealed class Request
{
[DataMember]
public string Id { get; set; }
public override string ToString()
{
return string.Format("Id: {0}", Id);
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="ServiceTest.Service">
<host>
<baseAddresses>
<add baseAddress="http://localhost:9090/webhost" />
</baseAddresses>
</host>
<endpoint binding="webHttpBinding" contract="ServiceTest.IService" />
</service>
</services>
</system.serviceModel>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
The Client
internal class Program
{
private static void Main(string[] args)
{
var client = new HttpClient();
var request = new Request {Id = "Nelibur"};
var result = client.PostAsync("http://localhost:9090/webhost/X/Y?Z=2000", CreateContent(request)).Result;
Console.WriteLine(result.Content.ReadAsStringAsync().Result);
Console.ReadKey();
}
private static StringContent CreateContent<T>(T value)
{
using (var stream = new MemoryStream())
{
var serializer = new DataContractJsonSerializer(typeof (T));
serializer.WriteObject(stream, value);
string content = Encoding.UTF8.GetString(stream.ToArray());
return new StringContent(content, Encoding.UTF8, "application/json");
}
}
}
Raw request:
POST http://localhost:9090/webhost/X/Y?Z=2000 HTTP/1.1
Content-Type: application/json; charset=utf-8
Host: localhost:9090
Content-Length: 16
Expect: 100-continue
Connection: Keep-Alive
{"Id":"Nelibur"}
You can try to use the request in for example Fiddler
Related
I designed a WCF REST service and try to consume it via JQuery in html page. When I added the html page in the solution where WCF service was created, I was getting the result as per the need. But when I created the HTML Page in a different solution I started getting errors such as no element found and the returned result is null.
I put a debugger in the service and I noticed that even if the the Consumer html page is kept in a different solution it still hits the global.asax file when service is called, But its not hitting any of the Method.
I even put "crossDomain:true," and "jQuery.support.cors = true;" in script for enabling Cross Domain problem as well as I have made changes in Global.asax file too but of no use.
Please provide me the solution for the same asap.
I am placing the actual code with configuration files and DataContracts along with the HTML code here :
ICouponService.cs :
[ServiceContract]
public interface ICouponService
{
[OperationContract]
[WebInvoke(UriTemplate = "/GenerateCoupon",
Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
CouponData GenerateCoupon(CouponData objCustomerData);
[OperationContract]
[WebInvoke(UriTemplate = "/UpdateGeneratedCoupon",
Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
ResponseMessage UpdateGeneratedCoupon(CouponData objCustomerData);
}
CouponService.cs :
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
public class CouponService : ICouponService
{
public CouponData GenerateCoupon(CouponData objCustomerData)
{
return objCouponData;
}
public ResponseMessage UpdateGeneratedCoupon(CouponData objCustomerData)
{
return objResponseMessage;
}
}
CouponData.cs :
[DataContract]
public class CouponData
{
[DataMember(EmitDefaultValue = false)]
public string Email { get; set; }
[DataMember(EmitDefaultValue = false)]
public string Points { get; set; }
[DataMember(EmitDefaultValue = false)]
public string ActiveFlag { get; set; }
[DataMember(EmitDefaultValue = false)]
public string CouponCode { get; set; }
[DataMember(EmitDefaultValue = false)]
public string RequestID { get; set; }
[DataMember(EmitDefaultValue = false)]
public string LoyaltyID { get; set; }
[DataMember(EmitDefaultValue = false)]
public string Result { get; set; }
[DataMember(EmitDefaultValue = false)]
public string ReturnFlag { get; set; }
[DataMember(EmitDefaultValue = false)]
public string UserName { get; set; }
[DataMember(EmitDefaultValue = false)]
public string Password { get; set; }
}
ResponseMessage.cs :
[DataContract]
public class ResponseMessage
{
[DataMember(EmitDefaultValue = false)]
public string Response { get; set; }
}
Global.asax :
First I thought there might be some Header problem so I added the same in Global file as shown below :
protected void Session_Start(object sender, EventArgs e)
{
string sessionId = Session.SessionID;
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();
EnableCrossDomainAjaxCall();
}
private void EnableCrossDomainAjaxCall()
{
if (!string.IsNullOrEmpty(Request.ServerVariables["HTTP_ORIGIN"]))
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Request.ServerVariables["HTTP_ORIGIN"]);
if (!string.IsNullOrEmpty(HttpContext.Current.Request.HttpMethod))
{
if (HttpContext.Current.Request.HttpMethod == "OPTIONS" || HttpContext.Current.Request.HttpMethod == "POST" || HttpContext.Current.Request.HttpMethod == "GET")
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
//HttpContext.Current.Response.End();
}
}
}
}
Web.config :
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="ServiceBehaviour" name="SAM_STORE_LoyaltyProgram_WCFLibrary.CouponService">
<endpoint address="" behaviorConfiguration="web" binding="webHttpBinding"
contract="SAM_STORE_LoyaltyProgram_WCFLibrary.ICouponService" />
</service>
<service name="SAM_STORE_LoyaltyProgram_WCFServices.qqq">
<endpoint address="" behaviorConfiguration="SAM_STORE_LoyaltyProgram_WCFServices.qqqAspNetAjaxBehavior"
binding="webHttpBinding" contract="SAM_STORE_LoyaltyProgram_WCFServices.qqq" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
<behavior name="SAM_STORE_LoyaltyProgram_WCFServices.qqqAspNetAjaxBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
<dataContractSerializer maxItemsInObjectGraph="5000" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
<bindings>
<webHttpBinding>
<binding maxBufferSize="5000" maxReceivedMessageSize="5000"
transferMode="Streamed">
<readerQuotas maxDepth="5000" maxStringContentLength="5000"
maxArrayLength="5000" maxBytesPerRead="5000" maxNameTableCharCount="5000" />
<security mode="None" />
</binding>
</webHttpBinding>
</bindings>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Index.html (script used to call service):
<script src="js/jquery-1.7.min.js" type="text/javascript"></script>
<script type="text/javascript">
var Type;
var Url;
var Data;
var ContentType;
var DataType;
var ProcessData;
var method;
var eMail;
var points;
var requestId;
var couponCode;
var returnFlag;
var userName;
var password;
//Generic function to call Service
function CallService() {
$.ajax({
type: Type,
url: Url,
data: Data,
contentType: ContentType,
dataType: DataType,
//crossDomain:true, --This is not working
processdata: ProcessData,
success: function (msg) {
ServiceSucceeded(msg);
},
error: function (msg) {
alert("Service Failed");
ServiceFailed(msg);
}
});
}
function SetGenerateCouponValues() {
eMail = $("#txtEmail").val();
points = $("#txtPoints").val();
requestId = $("#txtRequestId").val();
userName = 'username';
password = 'JEBtJHVu';
}
function DestroyGenerateCouponValues() {
eMail = null;
points = null;
requestId = null;
userName = null;
password = null;
}
function SetUpdateCouponValues() {
couponCode = $("#txtCode").val();
returnFlag = $("#txtFlag").val();
userName = 'username';
password = 'JEBtJHVu';
}
function DestroyUpdateCouponValues() {
couponCode = null;
returnFlag = null;
userName = null;
password = null;
}
function ServiceFailed(result) {
alert('Service call failed: ' + result.status + '' + result.statusText);
Type = null; Url = null; Data = null; ContentType = null; DataType = null; ProcessData = null;
}
function GenerateCoupon() {
SetGenerateCouponValues();
Type = "POST";
Url = "ServiceURL";
Data = '{ "Email": "' + eMail + '", "Points": "' + points + '", "UserName": "' + userName + '", "Password": "' + password + '", "RequestID": "' + requestId + '"}';
ContentType = "application/json; charset=utf-8";
DataType = "json";
ProcessData = true;
method = "GenerateCoupon";
CallService();
DestroyGenerateCouponValues();
}
function UpdateGeneratedCoupon() {
SetUpdateCouponValues();
Type = "POST";
Url = "ServiceURL";
Data = '{ "CouponCode": "' + couponCode + '", "ReturnFlag": "' + returnFlag + '", "UserName": "' + userName + '", "Password": "' + password + '"}';
ContentType = "application/json; charset=utf-8";
DataType = "json";
ProcessData = true;
method = "UpdateGeneratedCoupon";
CallService();
DestroyUpdateCouponValues();
}
function ServiceSucceeded(result) {
if (DataType == "json") {
if (method == "GenerateCoupon") {
var string = "Coupon Code : " + result.CouponCode + "<br/>" + "Points : " + result.Points + "<br/>" + "Result : " + result.Result;
$("#resultCreate").html(string);
$("#lblCouponCode").text(result.CouponCode);
$("#lblPoints").text(result.Points);
$("#lblResult").text(result.Result);
}
else if (method == "UpdateGeneratedCoupon") {
var string = "Response : " + result.Response;
$("#resultDelete").html(string);
$("#lblResponse").text(result.Response);
}
}
}
/*function ServiceFailed(xhr) {
alert("FAIL" + xhr.responseText);
if (xhr.responseText) {
var err = xhr.responseText;
if (err)
error(err);
else
error({ Message: "Unknown server error." })
}
return;
}*/
$(document).ready(
function () {
//jQuery.support.cors = true; --This is not working
$("#btnsubmit").click(function () {
GenerateCoupon();
});
$("#btnUpdateGeneratedCoupon").click(function () {
UpdateGeneratedCoupon();
});
});
</script>
Please provide me the solution for the same asap.
Is it calling ServiceSucceeded method??
If ServiceSucceeded called try to put $.support.cors = true; in CallService() starting and then check it
Try enabling WCF tracing and ETW tracing to see what is happening at the server side while invoking the service operation.
I read many tutorials but I could not get this running for my project. I made few GET services which send data and they work great, but I have problems receiving the data. I would really appreciate it if someone could tell me where I fail, instead of posting some links. :)
When I try to call the service in my browser I get the error: Method not allowed. But I Think this is just the first error.
Here is my code.
First the code in Android where I call the service:
public class MainActivity extends Activity {
String SERVICE_URI = "http://10.0.2.2:51602/RestServiceImpl.svc";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
HttpPost request = new HttpPost(SERVICE_URI + "/registerUser");
request.setHeader("Accept", "application/json");
request.setHeader("Content-type", "application/json");
try {
JSONStringer user = new JSONStringer()
.object()
.key("userInfo")
.object()
.key("Email").value("mail")
.key("Password").value("pass")
.key("Cauntry").value("country")
.key("UserName").value("username")
.endObject()
.endObject();
StringEntity entity = new StringEntity(user.toString());
request.setEntity(entity);
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(request);
} catch (JSONException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
IRestServiceImpl.cs:
[ServiceContract]
public interface IRestServiceImpl
{
[OperationContract]
[WebInvoke(Method = "POST",
UriTemplate = "registerUser",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
void receiveData(String data);
}
RestServiceImpl.cs:
public class RestServiceImpl : IRestServiceImpl
{
public void receiveData(String data)
{
//some code
}
}
Web.config:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="RestService.RestServiceImpl" behaviorConfiguration="ServiceBehaviour">
<endpoint address ="" binding="webHttpBinding" contract="RestService.IRestServiceImpl" behaviorConfiguration="web">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Your problem is that you're passing a JSON object to the operation, but the operation is expecting a JSON string. If I understand correctly the Java code, this is what you're sending:
{
"userInfo" : {
"Email" : "mail",
"Password" : "pass",
"Cauntry" : "country",
"UserName" : "username"
}
}
Which is not a JSON string.
There are a couple of things you can do. The first alternative is to modify the operation not to take a string, but to take a data contract which is equivalent to that object.
The code would look something like the one below. Notice that you should also change the body format to Bare (instead of Wrapped).
public class RequestData
{
public UserInfo userInfo { get; set; }
}
public class UserInfo
{
public string Email { get; set; }
public string Password { get; set; }
public string Cauntry { get; set; } // typo to match the OP
public string UserName { get; set; }
}
[ServiceContract]
public interface IRestServiceImpl
{
[OperationContract]
[WebInvoke(Method = "POST",
UriTemplate = "registerUser",
BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
void receiveData(RequestData data);
}
Another alternative, if the "schema" of the data changes every time, would be to take the input as a Stream (not a String). That way you should be able to accept basically any input. You may also need a content type mapper. You can find more information about this scenario at http://blogs.msdn.com/b/carlosfigueira/archive/2008/04/17/wcf-raw-programming-model-receiving-arbitrary-data.aspx.
I am trying to develop a WCF rest servie which needs authorization using custom attribute.
I want to send the response as 401 status code if the authorization key is not valid in custom attribute which implements IOperationBehavior and IParameterInspector.
can any one tell me how to send the 401 status code as response from the custom attribute.
Here is the implementation
public class AuthorizationAttribute : Attribute,IOperationBehavior,
IParameterInspector
{
#region IOperationBehavior Members
public void ApplyDispatchBehavior(OperationDescription operationDescription,
System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
{
dispatchOperation.ParameterInspectors.Add(this);
}
#endregion
#region IParameterInspector Members
public object BeforeCall(string operationName, object[] inputs)
{
string publicKey =
WebOperationContext.Current.IncomingRequest.Header["Authorization"];
if (publicKey == "592F2D7E-5E9C-400D-B0AE-1C2603C34137")
{
}
else
{
// Here i would like to send the response back to client
with the status code
}
}
return null;
}
#endregion
}
[Authorization]
public bool signin(string username, string password)
{
}
Throw a WebFormatException
throw new WebFaultException(HttpStatusCode.Unauthorized);
If you're using WCF Rest Starter kit you can also do:
throw new Microsoft.ServiceModel.Web.WebProtocolException(System.Net.HttpStatusCode.Unauthorized, "message", ex);
That method has some more overloads if you need.
public string CreateError(int code ,string description)
{
Context.Response.StatusCode = code;
Context.Response.StatusDescription = description;
Context.ApplicationInstance.CompleteRequest();
return null;
}
Then from your web service methods just use this to return errors example:
return CreateError(403, "Request denied by server");
If aspnetcompatibilityMode cannot be enabled in your WCF services, then you can do as below.
You have to intercept the message and set the status code in HttpResponseMessageProperty in the wcf message. I use a CustomErrorHandler for doing that and it works fine.
public class CustomErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
fault.Properties[HttpResponseMessageProperty.Name] = new HttpResponseMessageProperty()
{
StatusCode = statusCode
};
}
}
Below code is for injecting CustomErrorHandler into the ServiceBehavior.
public class CustomServiceBehaviour : IServiceBehavior
{
... other IServiceBehavior methods
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
channelDispatcher.ErrorHandlers.Add(new CustomErrorHandler());
}
}
}
Then in web.config use the serviceBehavior
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="CustomServiceBehavior" type="MyNamespace.CustomServiceBehavior, MyAssembly" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior name="CustomBehavior">
<CustomServiceBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
<service behaviorConfiguration="CustomBehavior" name="SomeService">
<endpoint ..../>
</service>
</system.serviceModel>
I'm trying to add message compression to my WCF service, but I'm kind of stuck...
So far I have:
An implementation of MessageEncoder:
public class CompressionEncoder : MessageEncoder
{
public CompressionEncoderFactory _factory;
public const string GZipContentType = "application/x-gzip";
public override string ContentType
{
get
{
return GZipContentType;
}
}
public override string MediaType
{
get { return ContentType; }
}
public override MessageVersion MessageVersion
{
get { return _factory.MessageVersion; }
}
public CompressionEncoder(CompressionEncoderFactory factory)
{
_factory = factory;
}
public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
{
var decompressedStream = new GZipStream(stream, System.IO.Compression.CompressionMode.Decompress, false);
XmlReader reader = XmlReader.Create(decompressedStream);
Message msg = Message.CreateMessage(reader, maxSizeOfHeaders, _factory.MessageVersion);
msg.Properties.Encoder = this;
return msg;
}
public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
{
MemoryStream stream = new MemoryStream(buffer.Array, buffer.Offset, buffer.Count);
GZipStream decompressedStream = new GZipStream(stream, System.IO.Compression.CompressionMode.Decompress);
return ReadMessage(decompressedStream, int.MaxValue);
}
public override void WriteMessage(Message message, Stream stream)
{
GZipStream compressedStream = new GZipStream(stream, System.IO.Compression.CompressionMode.Compress, true);
XmlWriter writer = XmlWriter.Create(compressedStream);
message.WriteMessage(writer);
}
public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
MemoryStream stream = new MemoryStream();
GZipStream compressedStream = new GZipStream(stream, System.IO.Compression.CompressionMode.Compress, true);
XmlWriter writer = XmlWriter.Create(compressedStream);
message.WriteMessage(writer);
writer.Close();
int messageLength = (int)compressedStream.Length;
byte[] messageBytes = new byte[messageLength];
compressedStream.Read(messageBytes, 0, messageLength);
int totalLength = messageLength + messageOffset;
byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
Array.Copy(messageBytes, 0, totalBytes, messageOffset, messageLength);
ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, messageLength);
return byteArray;
}
}
An implementation of MessageEncoderFactory:
public class CompressionEncoderFactory : MessageEncoderFactory
{
CompressionMessageEncodingBindingElement _element;
public CompressionEncoderFactory(CompressionMessageEncodingBindingElement element)
{
_element = element;
}
public override MessageEncoder Encoder
{
get { return new CompressionEncoder(this); }
}
public override MessageVersion MessageVersion
{
get { return _element.MessageVersion; }
}
}
An implementation of MessageEncodingBindingElement:
public sealed class CompressionMessageEncodingBindingElement : MessageEncodingBindingElement
{
MessageVersion _messageVersion = MessageVersion.None;
public override MessageEncoderFactory CreateMessageEncoderFactory()
{
return new CompressionEncoderFactory(this);
}
public override MessageVersion MessageVersion
{
get
{
return _messageVersion;
}
set
{
_messageVersion = value;
}
}
public override BindingElement Clone()
{
return new CompressionMessageEncodingBindingElement();
}
}
An implementation of BindingElementExtensionElement:
public class CompressionBindingElementExtensionElement : BindingElementExtensionElement
{
public override Type BindingElementType
{
get { return typeof(CompressionMessageEncodingBindingElement); }
}
protected override System.ServiceModel.Channels.BindingElement CreateBindingElement()
{
return new CompressionMessageEncodingBindingElement();
}
}
In the web.config I added this:
<service name="MyService">
<endpoint address="" binding="customBinding" bindingConfiguration="CompressionBinding" contract="IMyService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
...
<bindings>
<customBinding>
<binding name="CompressionBinding">
<messageCompression />
<httpTransport authenticationScheme="Anonymous"></httpTransport>
</binding>
</customBinding>
</bindings>
...
<extensions>
<bindingElementExtensions>
<add name="messageCompression" type="MyNamespace.CompressionBindingElementExtensionElement, MyNamespace" />
</bindingElementExtensions>
</extensions>
But when I run a client and call some service code, I get the following error:
The message version of the outgoing message (EnvelopeNone (http://schemas.microsoft.com/ws/2005/05/envelope/none) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)) does not match that of the encoder (Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing10 (http://www.w3.org/2005/08/addressing)). Make sure the binding is configured with the same version as the message.
What am I doing wrong?
Try changing MessageVersion _messageVersion = MessageVersion.Soap12WSAddressing10; at (3.).
Look at the Microsoft Technology Sample "TechnologySamples\Extensibility\MessageEncoder\Compression\".
How to set fall-back endpoint. I'm having more than one endpoint specified in the conifg file like follows. If the service was not accessible, then my client should check the next address specified in the list.
Client Configuration File:
<client>
<endpoint address="http://192.168.1.4/SampleAppWeb/Services/SampleAppService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISampleAppService"
contract="SampleAppServiceReference.ISampleAppService" name="BasicHttpBinding_ISampleAppService" />
<endpoint address="http://172.168.12.121/SampleAppWeb/Services/SampleAppService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISampleAppService"
contract="SampleAppServiceReference.ISampleAppService" name="BasicHttpBinding_ISampleAppService1" />
<endpoint address="http://127.0.0.1/Services/SampleAppWeb/SampleAppService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISampleAppService"
contract="SampleAppServiceReference.ISampleAppService" name="BasicHttpBinding_ISampleAppService2" />
<endpoint address="http://172.168.111.115/Services/SampleAppWeb/SampleAppService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISampleAppService"
contract="SampleAppServiceReference.ISampleAppService" name="BasicHttpBinding_ISampleAppService3" />
</client>
Code Behind:
var pass = new SampleAppServiceReference.SampleAppServiceClient("BasicHttpBinding_ISampleAppService3");
WCF itself doesn't have any built-in feature for this, but you can easily create a class which will do the retrying for you. The example below shows one such way. Notice that if your binding uses session, you may need to recreate the client (instead of simply reusing it), as its channel will likely be faulted if an error happens.
public class StackOverflow_4968244
{
[ServiceContract]
public interface ITest
{
[OperationContract]
int Add(int x, int y);
}
public class Service : ITest
{
public int Add(int x, int y)
{
Console.WriteLine("Request at service: {0}", OperationContext.Current.Host.BaseAddresses[0]);
if (new Random().Next(3) == 0)
{
throw new InvalidOperationException("Random error");
}
return x + y;
}
}
public class Client : ClientBase<ITest>, ITest
{
public Client(string address) : base(new BasicHttpBinding(), new EndpointAddress(address)) { }
public int Add(int x, int y)
{
return this.Channel.Add(x, y);
}
}
public class SafeClient : ITest
{
List<Client> clients;
public SafeClient(params Client[] clients)
{
this.clients = new List<Client>(clients);
}
public int Add(int x, int y)
{
foreach (var client in this.clients)
{
try
{
return client.Add(x, y);
}
catch (CommunicationException)
{
Console.WriteLine("Error calling client {0}, retrying with next one", client.Endpoint.Address.Uri);
}
}
throw new InvalidOperationException("All services seem to be down");
}
}
public static void Test()
{
string baseAddress1 = "http://" + Environment.MachineName + ":8000/Service";
string baseAddress2 = "http://" + Environment.MachineName + ":8001/Service";
string baseAddress3 = "http://" + Environment.MachineName + ":8002/Service";
ServiceHost host1 = new ServiceHost(typeof(Service), new Uri(baseAddress1));
ServiceHost host2 = new ServiceHost(typeof(Service), new Uri(baseAddress2));
ServiceHost host3 = new ServiceHost(typeof(Service), new Uri(baseAddress3));
host1.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
host2.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
host3.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
host1.Open();
host2.Open();
host3.Open();
Console.WriteLine("Hosts opened");
SafeClient safeClient = new SafeClient(
new Client(baseAddress1),
new Client(baseAddress2),
new Client(baseAddress3));
for (int i = 0; i < 20; i++)
{
Console.WriteLine(safeClient.Add(i, 10));
}
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host1.Close();
host2.Close();
host3.Close();
}
}