I am configuring a WCF client service programmatically, this is because we are executing libraries in a third-party sandbox where we have no control of the AppDomains configuration file.
The problem is that the standard 'public static void Configure(ServiceConfiguration config)' isn't being called automatically, this results in the client not being configured.
Here's the client with the static Configure method:
namespace OpenIt.service
{
public class CustomerClient : ClientBase<ICustomerClient>, ICustomerClient
{
public static void Configure(ServiceConfiguration config)
{
WebHttpBinding binding = new WebHttpBinding
{
TransferMode = TransferMode.Streamed
};
var serviceEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(ICustomerClient)), binding, new EndpointAddress(new Uri("http://localhost:3245")));
serviceEndpoint.Behaviors.Add(new WebHttpBehavior
{
DefaultOutgoingRequestFormat = System.ServiceModel.Web.WebMessageFormat.Json
});
config.AddServiceEndpoint(serviceEndpoint);
config.Description.Behaviors.Add(new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true });
}
public GetCustomerResponse GetCustomer(GetCustomerRequest request)
{
using (new OperationContextScope((IContextChannel)Channel))
{
GetCustomerResponse response = Channel.GetCustomer(request);
return response;
}
}
}
}
Here's the interface:
namespace OpenI
{
[ServiceContract]
public interface ICustomerClient
{
[OperationContract]
[WebInvoke(
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
Method = "POST",
UriTemplate = "rest/v6/customerMan/customer/GetCustomer")]
GetCustomerResponse GetCustomer(GetCustomerRequest request);
}
}
Here's the initialization and call:
private bool Testit()
{
//Here it fails due to no config
var client = new CustomerClient();
GetCustomerResponse response = client.GetCustomer(new GetCustomerRequest { CustomerNumber = "001" });
}
Does anyone have any idea why the 'public static void Configure(ServiceConfiguration config)' method isn't automatically being called? Am I missing something where 'CustomerClient()' is initialized under 'Testit()'.
The webservice is fine, tested with postman.
This method is used to configure WCF services, not clients.
Strong hint is that the parameter is of type ServiceConfiguration.
See the documentation for details.
To configure a client, you can use the methods (and constructors) on ClientBase and/or app.config settings (docs).
Related
have WCF server and WCF client. Below is the client code:
[GeneratedCode("System.ServiceModel", "3.0.0.0")]
[ServiceContract(ConfigurationName = "IMyService")]
public interface IMyService
{
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, UriTemplate = "DoSmth")]
[OperationContract(Action = "http://tempuri.org/IConfigService/SetSettings")]
OrigamiHttpResponse<List<ErrorRecords>> DoSmth(int param);
}
public class MyService: BaseClient<IMyService>
{
public ConfigServiceClient(AuthResult authObject) : base(authObject, null)
{
}
public OrigamiHttpResponse<List<ErrorRecords>> DoSmth(int param)
{
return Proxy.DoSmth(param);
}
}
public abstract class BaseClient<T> : BaseClient where T : class
{
protected T Proxy { get; private set; }
protected BaseClient(AuthResult authObject, IConfig config)
: base(authObject, config)
{
var factory = new ChannelFactory<T>("*");
if (factory.Endpoint == null || factory.Endpoint.Address == null)
throw new Exception("WCF Endpoint is not configured");
if (factory.Endpoint.Address.Uri.Scheme.ToLower() == "https")
HttpAccess.PrepareSslRequests();
if (_authObject != null)
{
var cb = new CookieBehavior(_authObject);
factory.Endpoint.Behaviors.Add(cb);
}
Proxy = factory.CreateChannel();
}
}
When I call method DoSmth() from console application, content type is json. But my architecture is that I am calling proxy method and then proxy server acts as client for wcf server and calls wcf method that is my DoSmth(). In this case content type is xml and i can't change it. May be the issue is in operation context, because it is one call from another. Could anyone help please?
This is caused by the fact that your WCF client (Proxy) is running in the operation context on the service method (which contains information about the incoming request), and that overrides the context which should be used by the outgoing request. To fix this you need to create a new operation context scope when doing the call, so that it will use the appropriate property from the WebInvoke/WebGet attributes:
public OrigamiHttpResponse<List<ErrorRecords>> DoSmth(int param)
{
using (new OperationContextScope((IContextChannel)Proxy))
{
return Proxy.DoSmth(param);
}
}
I'm trying to create a proxy WCF service over a WCF service using ChannelFactory.
I have defined the following interface for the WCF service to call. It has a generic GetResource method that writes directly into HttpContext the response byte[] and sets it's content type based on the type of the resource.
[ServiceContract]
public interface ITest
{
[OperationContract]
[WebInvoke(Method = "GET", UriTemplate = "getResource/?resourceKey={resourceKey}")]
void GetResource(string resourceKey);
}
Proxy implementation for GetResource:
public void GetResource(string resourceKey)
{
var factory = new ChannelFactory<ITest>(GetCustomBinding(), new EndpointAddress('https://test.com/mytest.svc'));
channel.Endpoint.Behaviors.Add(new WebHttpBehavior());
var channel = factory.CreateChannel();
using (var scope = new OperationContextScope((IContextChannel)channel))
{
channel.GetResource(resourceKey);
}
}
private static Binding GetCustomBinding()
{
var binding = new WebHttpBinding(WebHttpSecurityMode.Transport)
{
MaxReceivedMessageSize = 20000000,
MaxBufferSize = 20000000,
MaxBufferPoolSize = 20000000,
CloseTimeout = TimeSpan.FromMinutes(1)
};
binding.ContentTypeMapper = new JsonMapper();
return binding;
}
private class JsonMapper : WebContentTypeMapper
{
public override WebContentFormat GetMessageFormatForContentType(string contentType)
{
return WebContentFormat.Json;
}
}
The problem that I have is that when I'm calling the proxy at this endpoint, proxy.svc/GetResource?resourceKey="text.css" it returns the right content type but no actual content, ContentLength=0.
How should I handle Response Body forwarding? How do I read the response body? I tried using WebOperationContext.Current.IncomingResponse but it doesn't give me the response body as well.
I think you are attempting to use WCF in a way that it is not intended. A void method will return nothing. In theory, you can intercept this by adding an inspection behaviour, but not even sure that would work.
I have (potentially) similar code which loads file from a database and returns them, but that will return the content.
var file = ServiceUtilities.FileManager.GetFile(id);
if (file != null)
{
var fcr = new FileContentResult(file.Content, file.MimeType);
return fcr;
}
Perhaps you should revisit your intended approach.
I had a WCF service using CORS hosted on a console application that was working fine in .Net 4.5. the application is now required to work in .Net 3.5 and the preflight OPTIONS request is now failing for any POST request
Here is the interface for the services:
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
BiometricCaptureResponse Start(BiometricCaptureRequest biometricRequest);
[OperationContract]
[WebInvoke(Method = "OPTIONS", UriTemplate = "*")]
void Options();
The actual service:
public BiometricCaptureResponse Start(BiometricCaptureRequest biometricRequest)
{
//Stuff and things
}
public void Options()
{
return;
}
The Behaviour for CORS that gets applied to the requests and response
public string clientOrigin;
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HttpRequestMessageProperty requestProperty = request.Properties[HttpRequestMessageProperty.Name]
as HttpRequestMessageProperty;
if (requestProperty != null && requestProperty.Headers.AllKeys.Contains("Origin") && !string.IsNullOrEmpty(requestProperty.Headers["Origin"]))
{
clientOrigin = requestProperty.Headers["Origin"];
}
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
HttpResponseMessageProperty httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
if (httpHeader != null)
{
httpHeader.ApplyCrossOriginSupport(clientOrigin);
}
}
public static void ApplyCrossOriginSupport(this HttpResponseMessageProperty message, string origin)
{
message.Headers.Add("Access-Control-Allow-Origin", origin);
message.Headers.Add("Access-Control-Allow-Method", ConfigurationManager.AppSettings["Access-Control-Allow-Method"]);
message.Headers.Add("Access-Control-Allow-Headers", ConfigurationManager.AppSettings["Access-Control-Allow-Headers"]);
}
this all worked fin in 4.5 this is the process that would happen:
AfterReceiveRequest()
Options() service hit
BeforeSendReply()
AfterReceiveRequest()
Start()
BeforeSendReply()
Now in 3.5 the Ooptions Service will not get hit which will cause the whole request to fail, Is there anything I am missing to get this working for 3.5?
The Error I am getting from the console of Chrome is:
OPTIONS http://localhost:8502/testservice.svc/rest/Start net::ERR_CONNECTION_RESET
could this have anything to do with the WebInvoke attribute? It's MSDN page describes it as a 4.5/3.5 Client tech while I am only using .Net 3.5.
UPDATE:
Changing the following atrribute of the options service to use all methods
[WebInvoke(Method = "OPTIONS", UriTemplate = "*")]
[WebInvoke(Method = "*", UriTemplate = "*")]
has allowed option requests to hit the service, the application can work functionally like this but this is still not the optimal solution as it is not specific to an OPTIONS request, any reason behind this?
I’m afraid there is a little bug in the .Net 3.5 Version of the WebHttpDispatchOperationSelector.
The Method OPTIONS in combination with a * UriTemplate is not recognized by the selector.
As a workaround you could replace the default WebHttpBehavior with a custom version.
public class CorsWebHttpDispatchOperationSelector : WebHttpDispatchOperationSelector
{
private WebHttpDispatchOperationSelector target;
private ServiceEndpoint endpoint;
OperationDescription optionOperation;
public CorsWebHttpDispatchOperationSelector(ServiceEndpoint endpoint, WebHttpDispatchOperationSelector target)
{
this.target = target;
this.endpoint = endpoint;
foreach (var item in this.endpoint.Contract.Operations) {
var webInvoke = item.Behaviors.OfType<WebInvokeAttribute>().FirstOrDefault();
if (webInvoke != null && webInvoke.Method.Equals("options",StringComparison.OrdinalIgnoreCase) && webInvoke.UriTemplate == "*") {
optionOperation = item;
break;
}
}
}
#region IDispatchOperationSelector Members
protected override string SelectOperation(ref Message message, out bool uriMatched)
{
var result = target.SelectOperation(ref message);
var matched = message.Properties["UriMatched"] as bool?;
message.Properties.Remove("UriMatched");
message.Properties.Remove("HttpOperationName");
uriMatched = matched.HasValue && matched.Value;
var httpRequest = message.Properties["httpRequest"] as HttpRequestMessageProperty;
var cond = string.IsNullOrEmpty(result) &&
httpRequest != null &&
httpRequest.Method.Equals("options",StringComparison.OrdinalIgnoreCase);
if (cond && optionOperation != null) {
result = optionOperation.Name;
uriMatched = true;
}
return result;
}
#endregion
}
public class CorsWebHttpBehavior : WebHttpBehavior {
protected override WebHttpDispatchOperationSelector GetOperationSelector(ServiceEndpoint endpoint)
{
return new CorsWebHttpDispatchOperationSelector(endpoint, base.GetOperationSelector(endpoint));
}
}
I have an Apex Class (a class in SalesForce) that calls out to a REST web service.
public class WebServiceCallout
{
#future (callout=true)
public static void sendNotification(String aStr)
{
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http http = new Http();
req.setEndpoint('http://xx.xxx.xxx.xx:41000/TestService/web/test');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody(aStr); // I want to read this in the web service
try
{
res = http.send(req);
}
catch(System.CalloutException e)
{
System.debug('Callout error: '+ e);
System.debug(res.toString());
}
}
}
The REST web service (C#, WCF) looks like so:
public interface ITestService
{
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/test")]
string Test(string aStr);
}
The Test() method does primitive operations.
When I run
WebServiceCallout.sendNotification("a test message")
the POST gets to the web service but how can I read what was set in the body of the HttpRequest req that was set back in the sendNotification() method - req.setBody(aStr);?
That is, what should the parameter of string Test(string aStr); be?
Do I need to specify anything else such as any configs/attributes in my WebInvoke or the App.config (e.g. the binding)?
If you want to read the raw body of the incoming request, you should define the type of the parameter as a Stream, not string. The code below shows one way to implement your scenario, and the post at http://blogs.msdn.com/b/carlosfigueira/archive/2008/04/17/wcf-raw-programming-model-receiving-arbitrary-data.aspx has more information on this "raw" mode.
public class StackOverflow_25377059
{
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/test")]
string Test(Stream body);
}
public class Service : ITestService
{
public string Test(Stream body)
{
return new StreamReader(body).ReadToEnd();
}
}
class RawMapper : WebContentTypeMapper
{
public override WebContentFormat GetMessageFormatForContentType(string contentType)
{
return WebContentFormat.Raw;
}
}
public static void Test()
{
var baseAddress = "http://" + Environment.MachineName + ":8000/Service";
var host = new ServiceHost(typeof(Service), new Uri(baseAddress));
var binding = new WebHttpBinding { ContentTypeMapper = new RawMapper() };
host.AddServiceEndpoint(typeof(ITestService), binding, "").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
var req = (HttpWebRequest)HttpWebRequest.Create(baseAddress + "/test");
req.Method = "POST";
req.ContentType = "application/json";
var reqStream = req.GetRequestStream();
var body = "a test message";
var bodyBytes = new UTF8Encoding(false).GetBytes(body);
reqStream.Write(bodyBytes, 0, bodyBytes.Length);
reqStream.Close();
var resp = (HttpWebResponse)req.GetResponse();
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
foreach (var header in resp.Headers.AllKeys)
{
Console.WriteLine("{0}: {1}", header, resp.Headers[header]);
}
Console.WriteLine();
Console.WriteLine(new StreamReader(resp.GetResponseStream()).ReadToEnd());
Console.WriteLine();
}
}
By the way, your incoming request is not technically correct - you say (via Content-Type) that you're sending JSON, but the request body (a test message) is not a valid JSON string (it should be wrapped in quotes - "a test message" to be a JSON string instead).
I am using WCF .NET 4.0 hosting inside WebServiceHost. Normaly all works until i use my own defined class array inside class.
Server side function
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "foo")]
[OperationContract]
void Foo(FooQuery query);
Classes
[DataContract(Namespace="")]
public class FooQuery
{
[DataMember]
public MyFoo[] FooArray;
}
[DataContract(Namespace = "")]
public class MyFoo
{
[DataMember]
public string[] items;
}
Client side:
//create object
FooQuery myOriginalFoo = new FooQuery();
MyFoo _myFoo = new MyFoo();
_myFoo.items = new string[] { "one", "two" };
myOriginalFoo.FooArray = new MyFoo[] { _myFoo };
//serialize
var json = new JavaScriptSerializer().Serialize(myOriginalFoo);
string _text = json.ToString();
//output:
// {"FooArray":[{"items":["one","two"]}]}
var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://localhost:2213/foo");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
streamWriter.Write(_text);
streamWriter.Flush();
streamWriter.Close();
}
//here server give back: 400 Bad Request.
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
I have also tried manipulate my class with System.Runtime.Serialization.Json.DataContractJsonSerializer - all fine until i send to server and webinvoke give back error 400. Why webInvoke don't know how deserialize it or there any other error?
I found magic attribute called CollectionDataContract what makes a trick.
Adding new collection class:
[CollectionDataContract(Namespace = "")]
public class MyFooCollection : List<MyFoo>
{
}
Changed Query class
[DataContract(Namespace="")]
public class FooQuery
{
[DataMember]
public /*MyFoo[]*/MyFooCollection FooArray;
}
client code change:
MyFooCollection _collection = new MyFooCollection();
_collection.Add(_myFoo);
myOriginalFoo.FooArray = _collection; //new MyFoo[] { _myFoo };
All variables serialized now right :) yeah..takes many hours to figure out.
Since it is a web request, try GET instead:
[WebGet(ResponseFormat = WebMessageFormat.Json)]
Set the WebMessageBodyStyle to wrappedRequest as below for the WCF service to expect a wrapped JSON string. by default it expects a plain String.
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]