I have a wcfservice where it gets request and sends a response.
In the request object one of my element is of type DateTime.
When i am passing correct format of DateTime(eg:2012-12-30),the response object works fine and there are no issues.
But when i pass the element in correct format(eg: 201-12-30),the service gives exception and throws invalid date format exception.
Correct request:
<req:paymentDateDate>2012-12-12</req:paymentDateDate>
Bad request
<req:paymentDateDate>2012-12-12</req:paymentDateDate>
and the response will be
<InnerException i:nil="true"/>
<Message>String was not recognized as a valid DateTime.</Message>
how to validate it from code,I am validating for mindate as following:
else if (request.paymentRequest.payment.paymentDateDate == DateTime.MinValue )
{
FaultException fx = BillPayFaultResponse(request, "Schema Validation Failed", "Invalid content,element 'paymentDateDate' is required,but not found.");
throw fx;
}
how to validate for whether my service is receiving valid datetime string or not
If you just need to check your date for being in some date range, it is better for you to implement IParameterInspector. It will allow you to check parameters both on client and service sides.
Here is an example of DateTime Validation:
IParameterInspector & IOperationBehavior implementations:
public class DateTimeParameterInspector: IParameterInspector
{
private static readonly ILog Logger = LogManager.GetLogger(typeof (DateTimeParameterInspector));
private static readonly DateTime MinDateTime = new DateTime(1900, 1, 1);
private static readonly DateTime MaxDateTime = new DateTime(2100, 1, 1);
private readonly int[] _paramIndecies;
public DateTimeParameterInspector(params int[] paramIndex)
{
_paramIndecies = paramIndex;
}
public object BeforeCall(string operationName, object[] inputs)
{
try
{
foreach (var paramIndex in _paramIndecies)
{
if (inputs.Length > paramIndex)
{
var dt = (DateTime) inputs[paramIndex];
if (dt < MinDateTime || dt > MaxDateTime)
{
var errorMessage = String.Format(
"Invalid date format. Operation name: {0}, param index{1}", operationName, paramIndex);
Logger.Error(errorMessage);
throw new FaultException(errorMessage);
}
}
}
}
catch(InvalidCastException exception)
{
Logger.Error("Invalid parameter type", exception);
throw new FaultException(
"Invalid parameter type");
}
return null;
}
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{ }
}
public class DateTimeInspectorAttrubute: Attribute, IOperationBehavior
{
private readonly int[] _paramIndecies;
public DateTimeInspectorAttrubute(params int[] indecies)
{
_paramIndecies = indecies;
}
public void Validate(OperationDescription operationDescription)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.ParameterInspectors.Add(new DateTimeParameterInspector(_paramIndecies));
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
clientOperation.ParameterInspectors.Add(new DateTimeParameterInspector(_paramIndecies));
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
}
Here is an example of our parameter inspector usage:
[ServiceContract]
public interface IPricingService
{
[OperationContract]
[DateTimeInspectorAttrubute(0, 1)]
string GetPrice(DateTime startDate, DateTime endDate);
}
public class PricingService : IPricingService
{
public string GetPrice(DateTime startDate, DateTime endDate)
{
return "result";
}
}
In case you can have you your soap message invalide date, and you want to check it, you have to refer to IDispatchMessageInspector
To make the code to be compliled you have to reference System.ServiceModel, log4net (you can find it in nuget repository)
Related
I use orika 1.5.2 . when map data from HashMap to bean with convert String to java.util.Date, I get the Exception:
java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Date
DateToStringConverter also not take effect. But the exception not come out when I map data from Bean A to Bean B.
How can I convert String to Date when I map data from a Map to bean?
code:
public class UserA {
private Date birthday;
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
#Override
public String toString() {
return "UserA [birthday=" + birthday + "]";
}
}
public static void main(String[] args){
HashMap map = new HashMap();
map.put("birthday", "2014-04-28");
UserA ua = new UserA();
MapperFactory mapF = new DefaultMapperFactory.Builder().mapNulls(false).build();
mapF.getConverterFactory().registerConverter(new DateToStringConverter("yyyy-MM-dd"));
mapF.getMapperFacade().map(map, ua);
}
exception:
Exception
U need Object to Date converter, not DateToStringConverter
user:
#Data
public class UserA {
private Date birthday;
}
add Converter
public class ObjectToDateConverter extends CustomConverter<Object, Date> {
#Override
public Date convert(Object source, Type<? extends Date> destinationType, MappingContext context) {
if (source instanceof String) {
try {
return new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse((String) source);
} catch (ParseException e) {
return null;
}
}
return null;
}
}
test:
#Test
public void testMap(){
HashMap map = new HashMap();
map.put("birthday", "2018-10-14");
UserA ua = new UserA();
MapperFactory mapF = new DefaultMapperFactory.Builder().mapNulls(false).build();
mapF.getConverterFactory().registerConverter(new ObjectToDateConverter());
mapF.getMapperFacade().map(map, ua);
}
How can I handle an OnDeserialized event using MessageContract?
Need to do some data verification and transformation after receiving the message (but before executing methods).
For DataContract it was solved by declaration attribute.
But for MessageContract it doesn't work.
Is there any way to do this?
You are better of using WCF extension points instead of serialization. Specifically IOperationInvoker.
EDIT
Example:
Service and message definitions below. Note the new MyValidationBeforeInvokeBehavior attribute.
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
[MyValidationBeforeInvokeBehavior]
AddPatientRecordResponse AddPatientRecord(PatientRecord composite);
}
[MessageContract(IsWrapped = false, ProtectionLevel = ProtectionLevel.None)]
public class AddPatientRecordResponse
{
[MessageHeader(ProtectionLevel = ProtectionLevel.None)]
public Guid recordID;
[MessageHeader(ProtectionLevel = ProtectionLevel.None)]
public string patientName;
[MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
public string status;
}
[MessageContract(IsWrapped = false, ProtectionLevel = ProtectionLevel.None)]
public class PatientRecord
{
[MessageHeader(ProtectionLevel = ProtectionLevel.None)]
public Guid recordID;
[MessageHeader(ProtectionLevel = ProtectionLevel.None)]
public string patientName;
//[MessageHeader(ProtectionLevel = ProtectionLevel.EncryptAndSign)]
[MessageHeader(ProtectionLevel = ProtectionLevel.None)]
public string SSN;
[MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
public string comments;
[MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
public string diagnosis;
[MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
public string medicalHistory;
}
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public AddPatientRecordResponse AddPatientRecord(PatientRecord patient)
{
var response = new AddPatientRecordResponse
{
patientName = patient.patientName,
recordID = patient.recordID,
status = "Sucess"
};
return response;
}
}
Hook into wcf extensibility
public class MyValidationBeforeInvokeBehavior : Attribute, IOperationBehavior
{
public void Validate(OperationDescription operationDescription)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new MyValidationBeforeInvoke(dispatchOperation.Invoker);
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
}
Custom operation invoker:
public class MyValidationBeforeInvoke : IOperationInvoker
{
private readonly IOperationInvoker _original;
public MyValidationBeforeInvoke(IOperationInvoker original)
{
_original = original;
}
public object[] AllocateInputs()
{
return _original.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
var validator = new ValidatePatientRecord((PatientRecord) inputs[0]);
if (validator.IsValid())
{
var ret = _original.Invoke(instance, inputs, out outputs);
return ret;
}
else
{
outputs = new object[] {};
var patientRecord = (PatientRecord) inputs[0];
var returnMessage = new AddPatientRecordResponse
{
patientName = patientRecord.patientName,
recordID = patientRecord.recordID,
status = "Validation Failed"
};
return returnMessage;
}
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return _original.InvokeBegin(instance, inputs, callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return _original.InvokeEnd(instance, out outputs, result);
}
public bool IsSynchronous
{
get { return _original.IsSynchronous; }
}
}
The essence is that we never invoke the service call because it will never get there due to the validation error. We can also return what happened to the client.
Validation class and client call (for completes):
public class ValidatePatientRecord
{
private readonly PatientRecord _patientRecord;
public ValidatePatientRecord(PatientRecord patientRecord)
{
_patientRecord = patientRecord;
}
public bool IsValid()
{
return _patientRecord.patientName != "Stack Overflow";
}
}
client:
class Program
{
static void Main(string[] args)
{
var patient = new PatientRecord { SSN = "123", recordID = Guid.NewGuid(), patientName = "Stack Overflow" };
var proxy = new ServiceReference1.Service1Client();
var result = proxy.AddPatientRecord(patient);
Console.WriteLine(result.status);
Console.ReadLine();
}
}
after add my formatter to operation behavior :
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
ServerMessageFormatter Formatter = new ServerMessageFormatter();
dispatchOperation.Formatter = Formatter;
}
In Formatter I have empty Deserialize method cause I want to use default behavior
public void DeserializeRequest(System.ServiceModel.Channels.Message message, object[] parameters)
{}
but In Serialize
public System.ServiceModel.Channels.Message SerializeReply(System.ServiceModel.Channels.MessageVersion messageVersion, object[] parameters, object result)
{
//some code
}
Problem is that after enable this class, parameters in service method always was show as null, but in IDispatchMessageInspector class I see that parameters is send properly. I don't know why it's happening, I only add this message formatter code , it is possible that empty class for Deserialize causes this ?
When we are implementing IDispatchMessageFormatter interface usually we are not thinking that method DeserializeRequest is something important as it is not returning any data. This is misleading as method needs to do something.
The easiest way to make parameters correctly passed is to use base DispatchMessageFormatter. Add it to the constructor.
public class ResponseJsonFormatter : IDispatchMessageFormatter
{
IDispatchMessageFormatter basicDispatchMessageFormatter;
OperationDescription Operation;
public ResponseJsonFormatter(OperationDescription operation, IDispatchMessageFormatter inner)
{
this.Operation = operation;
this.basicDispatchMessageFormatter = inner;
}
public void DeserializeRequest(Message message, object[] parameters)
{
basicDispatchMessageFormatter.DeserializeRequest(message, parameters);
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
string json=Newtonsoft.Json.JsonConvert.SerializeObject(result);
byte[] bytes = Encoding.UTF8.GetBytes(json);
Message replyMessage = Message.CreateMessage(messageVersion, Operation.Messages[1].Action, new RawDataWriter(bytes));
replyMessage.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
return replyMessage;
}
}
And initiate it in the behavior:
public class ClientJsonDateFormatterBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
// throw new NotImplementedException();
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Formatter = new ResponseJsonFormatter(operationDescription, dispatchOperation.Formatter);
}
public void Validate(OperationDescription operationDescription)
{
// throw new NotImplementedException();
}
}
You can check the working example here in the github branch DateTimeFormatterWithParams
There is no default behavior if you do not provide your own logic on DeserializeRequest. You need to either reference the existing formatter and delegate manually in your ServerMessageFormater or provide your own logic.
I have a WCF Service, And I inspect that using MessageInspector ( inherit IDispatchMessageInspector)
I want to do some thing before running service and result of that ı want not run service.
I want to prevent client call but client dont receive exception.
Can you help me
This scenario looks like the post in the MSDN WCF forum entitled "IDispatchMessageInspector.AfterReceiveRequest - skip operation and manually generate custom response instead". If this is what you need (when you receive a message in the inspector, you decide that you want to skip the service operation, but return a message to the client and the client should not see an exception), then this answer should work for you as well. Notice that you'll need to create the response message in the same format as the client expects, otherwise you'll have an exception.
This code uses three of the (many) WCF extensibility points to achieve that scenario, a message inspector (as you've mentioned you're using), a message formatter and an operation invoker. I've blogged about them in an ongoing series about WCF extensibility at http://blogs.msdn.com/b/carlosfigueira/archive/2011/03/14/wcf-extensibility.aspx.
public class Post_55ef7692_25dc_4ece_9dde_9981c417c94a
{
[ServiceContract(Name = "ITest", Namespace = "http://tempuri.org/")]
public interface ITest
{
[OperationContract]
string Echo(string text);
}
public class Service : ITest
{
public string Echo(string text)
{
return text;
}
}
static Binding GetBinding()
{
BasicHttpBinding result = new BasicHttpBinding();
return result;
}
public class MyOperationBypasser : IEndpointBehavior, IOperationBehavior
{
internal const string SkipServerMessageProperty = "SkipServer";
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyInspector(endpoint));
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Formatter = new MyFormatter(dispatchOperation.Formatter);
dispatchOperation.Invoker = new MyInvoker(dispatchOperation.Invoker);
}
public void Validate(OperationDescription operationDescription)
{
}
class MyInspector : IDispatchMessageInspector
{
ServiceEndpoint endpoint;
public MyInspector(ServiceEndpoint endpoint)
{
this.endpoint = endpoint;
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
Message result = null;
HttpRequestMessageProperty reqProp = null;
if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name))
{
reqProp = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
}
if (reqProp != null)
{
string bypassServer = reqProp.Headers["X-BypassServer"];
if (!string.IsNullOrEmpty(bypassServer))
{
result = Message.CreateMessage(request.Version, this.FindReplyAction(request.Headers.Action), new OverrideBodyWriter(bypassServer));
}
}
return result;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
Message newResult = correlationState as Message;
if (newResult != null)
{
reply = newResult;
}
}
private string FindReplyAction(string requestAction)
{
foreach (var operation in this.endpoint.Contract.Operations)
{
if (operation.Messages[0].Action == requestAction)
{
return operation.Messages[1].Action;
}
}
return null;
}
class OverrideBodyWriter : BodyWriter
{
string bypassServerHeader;
public OverrideBodyWriter(string bypassServerHeader)
: base(true)
{
this.bypassServerHeader = bypassServerHeader;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement("EchoResponse", "http://tempuri.org/");
writer.WriteStartElement("EchoResult");
writer.WriteString(this.bypassServerHeader);
writer.WriteEndElement();
writer.WriteEndElement();
}
}
}
class MyFormatter : IDispatchMessageFormatter
{
IDispatchMessageFormatter originalFormatter;
public MyFormatter(IDispatchMessageFormatter originalFormatter)
{
this.originalFormatter = originalFormatter;
}
public void DeserializeRequest(Message message, object[] parameters)
{
if (message.Properties.ContainsKey(MyOperationBypasser.SkipServerMessageProperty))
{
Message returnMessage = message.Properties[MyOperationBypasser.SkipServerMessageProperty] as Message;
OperationContext.Current.IncomingMessageProperties.Add(MyOperationBypasser.SkipServerMessageProperty, returnMessage);
OperationContext.Current.OutgoingMessageProperties.Add(MyOperationBypasser.SkipServerMessageProperty, returnMessage);
}
else
{
this.originalFormatter.DeserializeRequest(message, parameters);
}
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
if (OperationContext.Current.OutgoingMessageProperties.ContainsKey(MyOperationBypasser.SkipServerMessageProperty))
{
return null;
}
else
{
return this.originalFormatter.SerializeReply(messageVersion, parameters, result);
}
}
}
class MyInvoker : IOperationInvoker
{
IOperationInvoker originalInvoker;
public MyInvoker(IOperationInvoker originalInvoker)
{
if (!originalInvoker.IsSynchronous)
{
throw new NotSupportedException("This implementation only supports synchronous invokers");
}
this.originalInvoker = originalInvoker;
}
public object[] AllocateInputs()
{
return this.originalInvoker.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
if (OperationContext.Current.IncomingMessageProperties.ContainsKey(MyOperationBypasser.SkipServerMessageProperty))
{
outputs = null;
return null; // message is stored in the context
}
else
{
return this.originalInvoker.Invoke(instance, inputs, out outputs);
}
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
throw new NotSupportedException();
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
throw new NotSupportedException();
}
public bool IsSynchronous
{
get { return true; }
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(ITest), GetBinding(), "");
endpoint.Behaviors.Add(new MyOperationBypasser());
foreach (var operation in endpoint.Contract.Operations)
{
operation.Behaviors.Add(new MyOperationBypasser());
}
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.Echo("Hello"));
Console.WriteLine("And now with the bypass header");
using (new OperationContextScope((IContextChannel)proxy))
{
HttpRequestMessageProperty httpRequestProp = new HttpRequestMessageProperty();
httpRequestProp.Headers.Add("X-BypassServer", "This message will not reach the service operation");
OperationContext.Current.OutgoingMessageProperties.Add(
HttpRequestMessageProperty.Name,
httpRequestProp);
Console.WriteLine(proxy.Echo("Hello"));
}
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
I am finally starting to get somewhere with WCF, but I have run into another problem. The response sent back does not include the header
<?xml version="1.0" encoding="utf-8" ?>
My Service Contract
[ServiceContract]
public interface IService1
{
// you can have optional parameters by simply specifying them and they will return null if there is nothing in there
[WebGet(UriTemplate="testing={value}", ResponseFormat = WebMessageFormat.Xml)]
[OperationContract]
XElement GetData(string value);
}
[XmlSerializerFormat]
public class Service1 : IService1
{
public XElement GetData(string value)
{
return new XElement("Somename", value);
}
}
returns this (3 is the value specified)
<Somename>3</Somename>
Is it also possible to easily wrap a response in a root element? Something like <response></response>?
responsThe result of calling the GetData method is the contents of what you return in the method. If you want a wrapper then return something like:
[XmlSerializerFormat]
public class Service1 : IService1
{
public XElement GetData(string value)
{
return new XElement("response",
new XElement("Somename", value));
}
}
EDIT:
To add the XML declaration (which actually may not be a good idea but you know best) do something like this:
var doc = new XDocument(
new XElement("response",
new XElement("Somename", value)));
doc.Declaration = new XDeclaration("1.0", "utf-8", "true");
return doc.Root;
I was looking for an answer to this myself, and stumbled upon this article:
http://shevaspace.blogspot.com/2009/01/include-xml-declaration-in-wcf-restful.html
It solved the problem in my situation, perhaps it is of help for others as well. It describes a custom attribute IncludeXmlDeclaration that you can stick on your method, and it will output the xml header.
For completeness, here is the code copied from the article:
public class XmlDeclarationMessage : Message
{
private Message message;
public XmlDeclarationMessage(Message message)
{
this.message = message;
}
public override MessageHeaders Headers
{
get { return message.Headers; }
}
protected override void OnWriteBodyContents(System.Xml.XmlDictionaryWriter writer)
{
// WCF XML serialization doesn't support emitting XML DOCTYPE, you need to roll up your own here.
writer.WriteStartDocument();
message.WriteBodyContents(writer);
}
public override MessageProperties Properties
{
get { return message.Properties; }
}
public override MessageVersion Version
{
get { return message.Version; }
}
}
public class XmlDeclarationMessageFormatter : IDispatchMessageFormatter
{
private IDispatchMessageFormatter formatter;
public XmlDeclarationMessageFormatter(IDispatchMessageFormatter formatter)
{
this.formatter = formatter;
}
public void DeserializeRequest(Message message, object[] parameters)
{
formatter.DeserializeRequest(message, parameters);
}
public Message SerializeReply(MessageVersion messageVersion, Object[] parameters, Object result)
{
var message = formatter.SerializeReply(messageVersion, parameters, result);
return new XmlDeclarationMessage(message);
}
}
public class IncludeXmlDeclarationAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Formatter = new XmlDeclarationMessageFormatter(dispatchOperation.Formatter);
}
public void Validate(OperationDescription operationDescription)
{
}
}