Get Response on a WCF client that get an HTTP 400 response code

I'm using next code (in .NET 7) as a client to an Java SOAP Web Service that I have no control:

namespace ActuacionMov
{
    
    
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.2.0-preview1.23462.5")]
    [System.ServiceModel.ServiceContractAttribute(Namespace="http://webservice", ConfigurationName="ActuacionMov.ActuacionPortType")]
    public interface ActuacionPortType
    {
        
        [System.ServiceModel.OperationContractAttribute(Action="urn:actuacion", ReplyAction="urn:actuacionResponse")]
        [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
        System.Threading.Tasks.Task<ActuacionMov.actuacionResponse> actuacionAsync(ActuacionMov.actuacionRequest request);
    }
    
    /// <remarks/>
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.2.0-preview1.23462.5")]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://webservice")]
    public partial class actuacion
    {
        
        private string datosField;
        
        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(Order=0)]
        public string datos
        {
            get
            {
                return this.datosField;
            }
            set
            {
                this.datosField = value;
            }
        }
    }
    
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.2.0-preview1.23462.5")]
    [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
    [System.ServiceModel.MessageContractAttribute(IsWrapped=false)]
    public partial class actuacionRequest
    {
        
        [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://webservice", Order=0)]
        public ActuacionMov.actuacion actuacion;
        
        public actuacionRequest()
        {
        }
        
        public actuacionRequest(ActuacionMov.actuacion actuacion)
        {
            this.actuacion = actuacion;
        }
    }
    
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.2.0-preview1.23462.5")]
    [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
    [System.ServiceModel.MessageContractAttribute(IsWrapped=false)]
    public partial class actuacionResponse
    {
        
        [System.ServiceModel.MessageBodyMemberAttribute(Name="actuacionResponse", Namespace="http://webservice", Order=0)]
        [System.Xml.Serialization.XmlArrayItemAttribute("result", IsNullable=false)]
        public string[] actuacionResponse1;
        
        public actuacionResponse()
        {
        }
        
        public actuacionResponse(string[] actuacionResponse1)
        {
            this.actuacionResponse1 = actuacionResponse1;
        }
    }
    
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.2.0-preview1.23462.5")]
    public interface ActuacionPortTypeChannel : ActuacionMov.ActuacionPortType, System.ServiceModel.IClientChannel
    {
    }
    
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.2.0-preview1.23462.5")]
    public partial class ActuacionPortTypeClient : System.ServiceModel.ClientBase<ActuacionMov.ActuacionPortType>, ActuacionMov.ActuacionPortType
    {
        
        /// <summary>
        /// Implement this partial method to configure the service endpoint.
        /// </summary>
        /// <param name="serviceEndpoint">The endpoint to configure</param>
        /// <param name="clientCredentials">The client credentials</param>
        static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);
        
        public ActuacionPortTypeClient(EndpointConfiguration endpointConfiguration) : 
                base(ActuacionPortTypeClient.GetBindingForEndpoint(endpointConfiguration), ActuacionPortTypeClient.GetEndpointAddress(endpointConfiguration))
        {
            this.Endpoint.Name = endpointConfiguration.ToString();
            ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
        }
        
        public ActuacionPortTypeClient(EndpointConfiguration endpointConfiguration, string remoteAddress) : 
                base(ActuacionPortTypeClient.GetBindingForEndpoint(endpointConfiguration), new System.ServiceModel.EndpointAddress(remoteAddress))
        {
            this.Endpoint.Name = endpointConfiguration.ToString();
            ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
        }
        
        public ActuacionPortTypeClient(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress) : 
                base(ActuacionPortTypeClient.GetBindingForEndpoint(endpointConfiguration), remoteAddress)
        {
            this.Endpoint.Name = endpointConfiguration.ToString();
            ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
        }
        
        public ActuacionPortTypeClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
                base(binding, remoteAddress)
        {
        }
        
        [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
        System.Threading.Tasks.Task<ActuacionMov.actuacionResponse> ActuacionMov.ActuacionPortType.actuacionAsync(ActuacionMov.actuacionRequest request)
        {
            return base.Channel.actuacionAsync(request);
        }
        
        public System.Threading.Tasks.Task<ActuacionMov.actuacionResponse> actuacionAsync(ActuacionMov.actuacion actuacion)
        {
            ActuacionMov.actuacionRequest inValue = new ActuacionMov.actuacionRequest();
            inValue.actuacion = actuacion;
            return ((ActuacionMov.ActuacionPortType)(this)).actuacionAsync(inValue);
        }
        
        public virtual System.Threading.Tasks.Task OpenAsync()
        {
            return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action<System.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndOpen));
        }
        
        private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
        {
            if ((endpointConfiguration == EndpointConfiguration.ActuacionHttpSoap11Endpoint))
            {
                System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
                result.MaxBufferSize = int.MaxValue;
                result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
                result.MaxReceivedMessageSize = int.MaxValue;
                result.AllowCookies = true;
                result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
                return result;
            }
            if ((endpointConfiguration == EndpointConfiguration.ActuacionHttpSoap12Endpoint))
            {
                System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding();
                System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();
                textBindingElement.MessageVersion = System.ServiceModel.Channels.MessageVersion.CreateVersion(System.ServiceModel.EnvelopeVersion.Soap12, System.ServiceModel.Channels.AddressingVersion.None);
                result.Elements.Add(textBindingElement);
                System.ServiceModel.Channels.HttpsTransportBindingElement httpsBindingElement = new System.ServiceModel.Channels.HttpsTransportBindingElement();
                httpsBindingElement.AllowCookies = true;
                httpsBindingElement.MaxBufferSize = int.MaxValue;
                httpsBindingElement.MaxReceivedMessageSize = int.MaxValue;
                result.Elements.Add(httpsBindingElement);
                return result;
            }
            throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
        }
        
        private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
        {
            if ((endpointConfiguration == EndpointConfiguration.ActuacionHttpSoap11Endpoint))
            {
                return new System.ServiceModel.EndpointAddress("https://desajboss.bilbokoudala.lan/ServicioActuacionMOV/services/Actuacion");
            }
            if ((endpointConfiguration == EndpointConfiguration.ActuacionHttpSoap12Endpoint))
            {
                return new System.ServiceModel.EndpointAddress("https://desajboss.bilbokoudala.lan/ServicioActuacionMOV/services/Actuacion");
            }
            throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
        }
        
        public enum EndpointConfiguration
        {
            
            ActuacionHttpSoap11Endpoint,
            
            ActuacionHttpSoap12Endpoint,
        }
    }
}

I works fine if the HTTP response code is between 200-299 or 500, but when code is, for example, 400, I get an exception ProtocolException with no information.

If I use the SOAP UI test client I can see the response with the description of the error. Also I was debugging the Microsoft internal code of the ClientBase class and I can see the error, but I notice that, even the response is accesible, but exception is throw first.

I tried to use a custom IClientMessageInspector created from an custom IEndpointBehavior in order to get the response in the AfterReceiveReply but this method is never invoked. Not like BeforeSendRequest that is invoked propertly.

The are the code I use:

static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials)
{
    serviceEndpoint.EndpointBehaviors.Add(new CustomEndpointBehavior());
}

public class CustomEndpointBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new CustomMessageInspector());
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
}

public class CustomMessageInspector : IClientMessageInspector
{
    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        // Nerver arrives here
        if (reply.IsFault)
        {
            // When HTTP 500 error no enter here
        }
    }

    public void BeforeSendRequest(ref Message request, object clientChannel)
    {
        // Always enter here
    }
}

Is there any way to be able to get the response?

Any way is way to alter the HTTP code received?

Thanks.

Upvotes: 0

Views: 70

Answers (2)

QI You
QI You

Reputation: 528

Good. I'm going to provide a way to generate log files on the client side as well.

public class WcfClientService
 {
     private readonly ServiceReference1.Service1Client _client;

     public WcfClientService()
     {
         AppContext.SetSwitch("System.ServiceModel.EnableDiagnostics", true);
      
         var binding = new BasicHttpBinding();
         var endpoint = new EndpointAddress("http://localhost:52316/Service1.svc");

     
         _client = new ServiceReference1.Service1Client(binding, endpoint);

         ConfigureWcfLogging();
     }

     private void ConfigureWcfLogging()
     {
         
       
         var traceSource = new TraceSource("System.ServiceModel", SourceLevels.All);

         
         traceSource.Listeners.Add(new XmlWriterTraceListener("ClientTrace.svclog"));

        
         Trace.AutoFlush = true;

         
         traceSource.TraceInformation("WCF Client Logging Enabled");
     }

     public async Task<string> CallServiceAsync(int value)
     {
         try
         {
             var result = await _client.GetDataAsync(value);
             Console.WriteLine($"Service Result: {result}");
             return result;
         }
         catch (Exception ex)
         {
             Trace.WriteLine($"Exception: {ex.Message}");
             throw;
         }
     }
 }

Controller

 public class HomeController : Controller
  {
      private readonly WcfClientService _wcfClientService;

      public HomeController()
      {
          _wcfClientService = new WcfClientService();
      }

      public async Task<IActionResult> Index()
      {
          await _wcfClientService.CallServiceAsync(123);
          return View();
      }
  }

Eventually, you'll find this log file in your project folder.

enter image description here

Upvotes: 0

QI You
QI You

Reputation: 528

First of all, the second question: The HTTP code itself is a standard protocol, and as a communication rule, it cannot be modified at will. However, when developing a web app, you can control how HTTP requests are responded to by customizing your server-side code. This includes returning different HTTP status codes, headers, and response body contents.

200-299: Successful response. So you can run normally.

500 Internal Server Error: The server encountered an unexpected condition and was unable to complete the request. is a generic error message that means that the server is unable to process the request, but the exact reason is not clear. You said you can work fine, I think you need to confirm again.

As for the first question, you can add a log file to see how the WCF service is performing.

Web.config

<system.diagnostics>
    <sources>
      <source name="System.ServiceModel" switchValue="Information,ActivityTracing"
        propagateActivity="true">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add initializeData="C:\logs\TracingAndLogging-service.svclog" type="System.Diagnostics.XmlWriterTraceListener"
        name="xml" />
    </sharedListeners>
    <trace autoflush="true" />
</system.diagnostics>

Upvotes: 0

Related Questions