What would be a better approach when providing a wcf client with the call result.
1. Wrapping the result in an object
public enum DefinedResult : short {
Success = 0,
TimeOut = 1,
ServerFailure = 2,
UserNotFount = 3,
Uknown = 4,
//etc.
}
[DataContract]
public class ServiceResult {
readonly DefinedResults dResult;
public ServiceResult(DefinedResult result) {
this.dResult = result;
}
[DataMember]
public bool IsSuccess
{
get {return this.dResult == DefinedResult.Success;}
}
}
//Client:
WcfClient client = new WcfClient();
ServiceResult result = client.DoWork();
2. Throwing a custom Exception:
[Serializable]
public UserNotFoundException: Exception {
public UserNotFoundException(string message): base(message) {}
}
//client:
WcfClient client = new WcfClient();
try {
result = client.DoWork();
}
catch(FaultException<ExceptionDetail> ex) {
switch(ex.Detail.Type)
{
case "MyCompany.Framework.Exceptions.UserNotFound":
//handle
break;
case "MyCompany.Framework.Exceptions.ServerError":
//handle
break;
}
}
Now, the client can be another .net process (server side) or the same service can be called by java script, hence the question - which one of these (or may be there is something better) is a better approach to let the client know of what happened with the call?
First of all, it depends: if you want to return a condition which is not exceptional, then use a result value. Otherwise, use exceptions. In WCF, it goes like this:
Create a custom exception class:
[DataContract]
class MyException : FaultException<mydetails>
Define that your service throws it:
[FaultContract(...)]
void mymethod()...
throw MyException in your service method
Then you can catch your exception in the service method like catch FaultException<mydetails>
This is the nicest way there is.
FaultExceptions are swallowed by WebHttpBinding (required for JSON/REST services). In this case, if you want to provide detailed infos to your client, Option 1 is better.
If JSON is not in the way, I would recommend Option 2.
Related
My goal is to call api (via post), accept payload as base type and later cast it to concrete type. If I do that from main solution (where my api stands), everything works well. But I can't understand why same code doesn't work from other solutions.
So I have my request (declared in different solutions)
namespace Nb
{
public class NbRequestBase
{
public string BaseProp { get; set; }
}
public class NbRequestConcrete : NbRequestBase
{
public string ConcreteProp { get; set; }
}
}
And this is my endpoint:
[HttpPost]
[Route("payments/nb")]
public IHttpActionResult Prepare(NbRequestBase request)
{
if(request is NbRequestConcrete)
{
}
try
{
// <<< INSERT CODE HERE >>>
NbRequestConcrete nbRequestConcrete = (NbRequestConcrete)request;
return Ok();
}
catch (Exception ex)
{
_logger.Error(ex);
return InternalServerError();
}
}
and this is my calling code:
NbRequestConcrete requestTwo = new NbRequestConcrete()
{
BaseProp = "BaseProp",
ConcreteProp = "ConcreteProp"
};
using (var client = new HttpClient())
{
var _clientId = "_clientId";
var _clientSecret = "_clientSecret";
client.BaseAddress = new Uri("http://localhost:50228");
#region Formatter
JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter();
formatter.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
List<MediaTypeFormatter> formatters = new List<MediaTypeFormatter>();
formatters.Add(formatter);
#endregion
var responseMessage = client.PostAsync($"payments/nb?clientId={_clientId}&clientSecret={_clientSecret}", requestTwo, formatter).Result;
responseMessage.EnsureSuccessStatusCode();
}
If I put my calling code into other project/solution (for example just new console app), API endpoint is hit, but payload is null.
payload when called form console app
If I put exacly same calling code into project where my api is (for example in same API endpoint method, at try/catch block start and call it again), API endpoint is hit, payload is NOT null and casting works. Why is it? And how to fix it?
payload when called from same solution try/catch start
And BTW. How to make this call via postman?
Regards
This line tells the model binder to set the values of any matching properties in request to the value that was passed to the API:
public IHttpActionResult Prepare(NbRequestBase request)
The model binder does not attach all the other properties to the request, because it has no idea what they would be.
Problem was Assemblies name where NbRequestConcrete in console app lived in one assembly and on API lived in other. So request was different.
{
"$type": "Nb.NbRequestConcrete, Tester",
"ConcreteProp": "ConcreteProp",
"BaseProp": "BaseProp"
}
VS
{
"$type": "Nb.NbRequestConcrete, MYApi",
"ConcreteProp": "ConcreteProp",
"BaseProp": "BaseProp"
}
I wanted to understand whether there are any better way to do logging or error handling in customized way with WCF
Here is the scenario.
I have a service as below
namespace IntegrationServices.Contract.SomeServices
{
[ServiceContract(Name = "SomeServices")]
public interface ISomeService
{
//Having 30+ contracts below is one of them
[OperationContract]
[WebInvoke(UriTemplate = "/GetOnlineSomething")]
SomeTransactionResponse GetOnlineSomething(string someNumber);
}
}
Which is implemented by below calss
namespace IntegrationServices.Service.PaymentServices
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
[GlobalErrorBehaviorAttribute(typeof(GlobalErrorHandler), Project.Name)]
public class PaymentService : ISomeService
{
public OnlinePaymentTransactionResponse GetOnlinePaymentTransaction(string someNumber)
{
//we have authentication code here which is OK
//Logging the request
_transactionKey = Guid.NewGuid();
TransactionRequest(/*some message and some parameter*/);
try
{
//do something
}
catch (Exception ex)
{
LogHelper.WriteErrorLogAsync(/*logging some more information*/);
response.ErrorMessage = Project.PHAPICommonErrorMessage;
}
//Logging the response
TransactionResponse(/*some parameter and error message from catch block*/);
return response;
}
}
}
Logging Function is as below
private void TransactionRequest(string xmlObject, Guid? groupKey, string name)
{
//writing to DB
}
private void TransactionResponse(string xmlObject, Guid? groupKey, string name)
{
//writing to DB
}
Now my question here is, I have to write in all 30+ function to log request and response like above.
Can anybody help me to how I can improve above or need to redesign whole approach.
I've had great success with using PostSharp for logging in my code bases. In the context of WCF its similar to the IServiceBehavior approach suggested by Aleksey L in that it gives you "hooks" that execute before and after the method's execution in which you can add your logging. The benefit comes in that you can also use the PostSharp logging attribute outside the context of WCF call.
I am creating an application that consumes a SOAP web service in C#. I generated a proxy class for the web service WSDL using the svcutil tool.
I added the proxy class to my code and I am using it to make calls to the web service and get results asynchrounsly.
Everything works pretty fine when the client has an Internet access. However, if I run attempt to access while the application doesn't have Internet access it crashes raising the following exception:
An exception of type 'System.ServiceModel.EndpointNotFoundException' occurred in
System.ServiceModel.Internals.dll but was not handled in user code
I am trying to catch this exception to prevent the application from crashing and provide the user with a more friendly error message, However, since I am doing async web calls, simply surrounding the web service calls by a try- catch does not help.
According to the exception details it happens in the End_FunctionName function that is defined inside the auto-generated proxy file.
Any tips about how to be able to gracefully handle this exception ?
Its pretty difficult to know exactly what is happening; however, I'm going to assume you have a web service like such
[ServiceContract]
public interface IMyService
{
[OperationContract]
String Hello(String Name);
[OperationContract]
Person GetPerson();
}
You probably have a proxy like this :
public class MyPipeClient : IMyService, IDisposable
{
ChannelFactory<IMyService> myServiceFactory;
public MyPipeClient()
{
//This is likely where your culprit will be.
myServiceFactory = new ChannelFactory<IMyService>(new NetNamedPipeBinding(), new EndpointAddress(Constants.myPipeService + #"/" + Constants.myPipeServiceName));
}
public String Hello(String Name)
{
//But this is where you will get the exception
return myServiceFactory.CreateChannel().Hello(Name);
}
public Person GetPerson()
{
return myServiceFactory.CreateChannel().GetPerson();
}
public void Dispose()
{
((IDisposable)myServiceFactory).Dispose();
}
}
If you have an error connecting you will get it not when you try to connect to the channel factory but when you actually try to call a function.
To fix this problem, you can put a try catch around every single function call and handle async calls manually.
Conversely, you can have a function like init() that is called synchronously every single time you instantiate a connection. This way you know that if that call connects that you have a connection.
If you are at risk of a connection dropping at any time I advise you go with the former option.
Anyway here is an example of how you'd fix it:
public class MyPipeClient : IMyService, IDisposable
{
ChannelFactory<IMyService> myServiceFactory;
public MyPipeClient()
{
myServiceFactory = new ChannelFactory<IMyService>(new NetNamedPipeBinding(), new EndpointAddress(Constants.myPipeService + #"/" + Constants.myPipeServiceName + 2) );
}
public String Hello(String Name)
{
try
{
return Channel.Hello(Name);
}
catch
{
return String.Empty;
}
}
public Person GetPerson()
{
try
{
return Channel.GetPerson();
}
catch
{
return null;
}
}
public Task<Person> GetPersonAsync()
{
return new Task<Person>(()=> GetPerson());
}
public Task<String> HelloAsync(String Name)
{
return new Task<String>(()=> Hello(Name));
}
public void Dispose()
{
myServiceFactory.Close();
}
public IMyService Channel
{
get
{
return myServiceFactory.CreateChannel();
}
}
}
I uploaded the source I wrote so that you could download the full source. You can get it here : https://github.com/Aelphaeis/MyWcfPipeExample
PS : This Repository throws the exception you are getting. In order to remove it just go to MyPipeClient and remove the + 2 in the constructor.
If you are using a Duplex, Consider using this repository:
https://github.com/Aelphaeis/MyWcfDuplexPipeExample
What's the best way to unit test expected faults from WCF services?
I am attempting to unit test a WCF service which is (correctly) throwing FaultExceptions for a certain reproducible error. The unit tests get an instance of the WCF client and call the applicable service method, which throws a FaultException.
All of that works as you would expect, but I am having difficulty unit testing this, because the fault causes the IDE to break when the error isn't caught in the service implementation. Because I am using faults, and not exceptions, I was expecting the IDE to serialize the exception and send it to the client, where it would raise an exception.
I do see that there is a configuration option to disable breaking for specific user-unhandled exceptions, but I was hoping somebody could point out a better way to achieve the same results, as this isn't easily doable in a team environment.
Here's some sample code of what the implementation currently looks like...
The unit test project has a service reference to my WCF service, and I have defined the interface as such:
[OperationContract(Name = "DoSomething")]
[FaultContract(typeof(EpicFail))]
ResponseObject DoSomething(RequestObject requestObject);
The fault is defined as such:
[DataContract]
public class EpicFail
{
public EpicFail(string action)
{
this.Reason = "Epic Fail";
this.Action = action;
}
[DataMember]
public string Reason
{
get;
set;
}
[DataMember]
public string Action
{
get;
set;
}
}
The code that calls the service looks vaguely like this:
[TestMethod()]
[ExpectedException(typeof(FaultException<EpicFail>))]
public void FaultTest_Fails_Epicly()
{
bool testPassed = false;
try
{
ResponseObject resp = GetServiceClient().DoSomething(req);
}
catch (FaultException<EpicFail>)
{
testPassed = true;
}
Assert.IsTrue(testPassed);
}
I edited the code to show that I am using the ExpectedException attribute and it doesn't seem to be having much effect on keeping the IDE/Debugger from breaking when the exception is thrown in the service.
You can always use ExpectedExceptionAttribute (in NUnit) to make sure this is the exception thrown. MSTest has similar concept as well.
[ExpectedException(typeof(MyException))]
void my_test()
{
// test
}
If you have some Mock verification to do, I would use try/catch block and verify in the catch and then throw the exception.
UPDATE
When you are using ExpectedException attribute, you are not supposed to catch the exception, instead you need to let the NUnit that runs your test to catch it.
If you need to verify special information in the exception then you catch the exception, verify the information and then rethrow:
[ExpectedException(typeof(MyException))]
void my_test()
{
try
{
// call the service
}
catch(MyException ex)
{
Assert.IsTrue(ex.Message.Contains("error code 200"));
throw ex;
}
}
mattv,
Why does this test has to access the service remotely? From what I see your code:
ResponseObject resp = GetServiceClient().DoSomething(req);
Is somehow getting a service client, and not a service instance itself. I'd advise to test the service concrete class directly for unit tests.
However, if you need this scenario, have you tried NOT CATCHING the exception and running the test? Does it give the same result?
And by the way, if you need to catch and rethrow use the following pattern:
try {
//Do something
}
catch(SomeException e) {
//Do something with e
throw
}
Hopefully there are some WCF wizards out there that can spot my mistake here.
I am trying to set up a global error handler via an IErrorHandler based behaviorExtension on a RESTful JSON WCF Service. The method is decorated as such:
[OperationContract]
[WebGet(UriTemplate = "screens/info", ResponseFormat = WebMessageFormat.Json)]
The IErrorHandler implementation is:
public class ErrorHandler : IErrorHandler
{
public void ProvideFault(Exception error,
MessageVersion version,
ref Message fault)
{
var error = new JsonError
{
Message = error.Message,
FaultCode = -1,
StackTrace = error.StackTrace
};
fault = Message.CreateMessage(version,
"",
ideaScreeningError,
new DataContractJsonSerializer(
ideaScreeningError.GetType()));
// tell WCF to use JSON encoding rather than default XML
var wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);
fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);
//Modify response
var rmp = new HttpResponseMessageProperty
{
StatusCode = HttpStatusCode.BadRequest,
StatusDescription = "Bad Request"
};
fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);
}
public bool HandleError(Exception error)
{
return true;
}
}
I can verify (via breakpoints) that the extension is being called and is executing properly. When I look at the result of the AJAX call in the browser, I can see that WCF is still returning a 500 Internal Server Error rather than the Fault details that I've specified in the error handler.
If I change Exception types being thrown in the WCF method, those are reflected in the result in the browser so I can surmise that WCF is doing something to handle the Exception and return something internally.
How do I make it stop!?
EDIT
I'm adding the custom Behavior Element:
public class ErrorBehaviorElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new ErrorBehavior();
}
public override Type BehaviorType
{
get { return typeof(ErrorBehavior); }
}
}
And Behavior:
internal class ErrorBehavior : WebHttpBehavior
{
protected override void AddServerErrorHandlers(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher)
{
// clear default error handlers.
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Clear();
// add the Json error handler.
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(
new ErrorHandler());
}
}
The issue here lies with the WCF Rest Starter Kit (which I didn't realize was in use since I didn't start this project), more specifically WebServiceHost2. I opened the ServiceHost in Reflector and found this lovely little piece of code in OnOpening():
if (endpoint.Behaviors.Find<WebHttpBehavior>() != null)
{
endpoint.Behaviors.Remove<WebHttpBehavior>();
WebHttpBehavior2 item = new WebHttpBehavior2();
// other code omitted
endpoint.Behaviors.Add(item);
}
As you can see, no matter what behavior you want added to the endpoint, as long as it inherits from WebHttpBehavior the Rest Start Kit components will hijack your handler, remove it, and replace it with its own.
Keep in mind that WebHttpBehavior2 also inherits from WebHttBehavior so inheriting from WebHttpBehavior2 in my extension did nothing to help the matter.
The first step was to create a new WebSeriveHost that derived from WebServiceHost2 and overrode OnOpening() and re-hijack what the Rest Starter Kit stole from me:
if(endpoint.Behaviors.Find<WebHttpBehavior>() != null)
{
endpoint.Behaviors.Remove<WebHttpBehavior>();
var item = ErrorBehavior();
// other code
endpoint.Behaviors.Add(item);
}
And then create a new WebServiceHostFactory that returned my custom WebServiceHost type.
Don't forget to set the ContentType of the response as well:
rmp.Headers[HttpResponseHeader.ContentType] = "application/json";
Based on comments I would try to remove common webHttpBehavior. You have defined your own behavior derived from webHttp. There is no reason to have two webHttp behaviors in your service configuration. Moreover webHttp behavior adds its own error handler which behaves exactly as you describe. Maybe it will not help but you can give it a try.