(Another) The remote server returned an unexpected

2019-08-30 07:41发布

问题:

The reason I am asking this as a separate question to the multitude of others is that I am using Castle Windsor 3.0 as my DI framework and thus configuring my endpoints through the WCF Facility of CW. I am unable to find any resolution with this setup.


:: Update ::

Thanks for the comments.

The project is a standard WCF Service Application, which feeds off a number of standard class libraries connecting to underlying funcitonality (SQl Server etc). The Web services that exist in the project are standard Wcf Services (.svc) and will be hosted in IIS (tested in the default VS debugging server) will be consumed by an ASP.NET MVC3 Web Application.

The services are hooked into the Windsor Container at both client and service sides of this.

Service side:

<%@ ServiceHost 
Language="C#" 
Debug="true" 
Service="FileDownloadService" 
CodeBehind="FileDownloadService.svc.cs" 
Factory="Castle.Facilities.WcfIntegration.DefaultServiceHostFactory, 
    Castle.Facilities.WcfIntegration" %>

Thus, the Windsor WCFacility is responsible for all dependency injection resolution in both the MVC3 app, and the WCF Service Application.

The main reason I am concerned over the configuration (which I know uses values which I have obtained form tutorials/walkthroughs/SO questions) is because I am unsure if Windsor is 100% picking this configuration up on the service side - opinions?

I have also updated the code snippets to show the current impl.

[DataContract]
    public enum FileTypeEnum
    {
        [EnumMember]
        Generic = 1,

        [EnumMember]
        TXT = 2,

        [EnumMember]
        XLS = 3,

        [EnumMember]
        PDF = 4,

        [EnumMember]
        DOC = 5
    }

The Web Service solution, which has the WCF web service in, defines the following contract:

[ServiceContract]
public interface IFileDownloadService
{
    [OperationContract]
     FileDownloadReturnMessage DownloadFile(FileDownloadMessage request);
}

The return type contract is:

[MessageContract]
public class FileDownloadReturnMessage : IDisposable
{
    public FileDownloadReturnMessage(FileMetaData metaData, Stream stream)
    {
        FileByteStream = stream;
    }

    [MessageBodyMember(Order = 1)]
    public Stream FileByteStream;

    [MessageHeader(MustUnderstand = true)]
    public FileMetaData DownloadedFileMetadata;

    public void Dispose()
    {
        if (FileByteStream != null)
        {
            FileByteStream.Close();
            FileByteStream = null;
        }
    }

and the request contract is:

[MessageContract]
public class FileDownloadMessage
{
    [MessageHeader(MustUnderstand = true)]
    public FileMetaData FileMetaData;
}

and:

[DataContract(Namespace = "http://schemas.acme.it/2009/04")]
public class FileMetaData
{
    public FileMetaData(string fileName, string remoteFilePath)
    {
        FileName = fileName;
        RemoteServerFilePath = remoteFilePath;
        FileType = FileTypeEnum.Generic;
    }

    public FileMetaData(string fileName, string remoteFilePath, FileTypeEnum? fileType)
    {
        FileName = fileName;
        RemoteServerFilePath = remoteFilePath;
        FileType = fileType;
    }

    [DataMember(Name = "FileType", Order = 0, IsRequired = true)]
    public FileTypeEnum? FileType;

    [DataMember(Name = "FileName", Order = 1, IsRequired = true)]
    public string FileName;

    [DataMember(Name = "RemoteFilePath", Order = 2, IsRequired = true)]
    public string RemoteServerFilePath;
}

The configuration on the server for the Windsor injected service is:

.Register(Component.For<IFileDownloadService>()
       .ImplementedBy<FileDownloadService>()
       .Named("FileDownloadService")
       .AsWcfService(new DefaultServiceModel()
       .AddEndpoints(WcfEndpoint
              .BoundTo(new BasicHttpBinding
                   {
                       MaxReceivedMessageSize = 2147483647,
                       MaxBufferSize = 2147483647,
                       MaxBufferPoolSize = 2147483647,
                       TransferMode = TransferMode.Streamed,
                       MessageEncoding = WSMessageEncoding.Mtom,
                       ReaderQuotas = new XmlDictionaryReaderQuotas
                            {
                              MaxDepth = 2147483647,
                              MaxArrayLength = 2147483647,
                              MaxStringContentLength = 2147483647,
                              MaxNameTableCharCount = 2147483647,
                              MaxBytesPerRead = 2147483647
                            }
                   }))
              .Hosted()
              .PublishMetadata())
      .LifeStyle.PerWcfOperation())

and the client configuration for the endpoint is:

_container.Register(Component.For<IFileDownloadService>()
                    .AsWcfClient(new DefaultClientModel
                        {
                            Endpoint = WcfEndpoint
                                .BoundTo(new BasicHttpBinding
                                    {
                                        MaxReceivedMessageSize = 2147483647,
                                        MaxBufferSize = 2147483647,
                                        MaxBufferPoolSize = 2147483647,
                                        TransferMode = TransferMode.Streamed,
                                        MessageEncoding = WSMessageEncoding.Mtom,
                                        ReaderQuotas = new XmlDictionaryReaderQuotas
                                            {
                                                MaxDepth = 2147483647,
                                                MaxArrayLength = 2147483647,
                                                MaxStringContentLength = 2147483647,
                                                MaxNameTableCharCount = 2147483647,
                                                MaxBytesPerRead = 2147483647
                                            }
                                    })
                                .At(ConfigurationManager.AppSettings["FileDownloadAddress"])
                            }));

As far as I know these endpoint configurations have to match, which they do. But for some reason hitting the method:

var commandResult = _downloadService.DownloadFile(command);

results in an exception with the following stack trace:

Ex Message:     The remote server returned an unexpected response: (400) Bad Request.
Source:     Castle.Facilities.WcfIntegration
Target Site:    Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor+<>c__DisplayClass1 -> Void <PerformInvocation>b__0(Castle.Facilities.WcfIntegration.WcfInvocation)
Stack Trace:    at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.<>c__DisplayClass1.<PerformInvocation>b__0(WcfInvocation wcfInvocation)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.ApplyChannelPipeline(Int32 policyIndex, WcfInvocation wcfInvocation, Action`1 action)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.<>c__DisplayClass4.<ApplyChannelPipeline>b__3()
   at Castle.Facilities.WcfIntegration.WcfInvocation.Proceed()
   at Castle.Facilities.WcfIntegration.RepairChannelPolicy.Apply(WcfInvocation wcfInvocation)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.ApplyChannelPipeline(Int32 policyIndex, WcfInvocation wcfInvocation, Action`1 action)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.PerformInvocation(IInvocation invocation, IWcfChannelHolder channelHolder, Action`1 action)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.PerformInvocation(IInvocation invocation, IWcfChannelHolder channelHolder)
   at Castle.Facilities.WcfIntegration.Async.WcfRemotingAsyncInterceptor.PerformInvocation(IInvocation invocation, IWcfChannelHolder channelHolder)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.IWcfChannelHolderProxy_2.FakeDownloadTest(FakeDownloadTestRequest request)
   at cpfe.DAL.Repositories.FileDownloadRepository.DownloadFile(IEnumerable`1 fileIds, TenantDTO tenant, String zipPackageName, UserDTO user) \..\..\..\FileDownloadRepository.cs:line 44

Does anybody have any clue as to why this is happening?

Thanks in advance!

回答1:

I reproduced 400 error and it is gone after a few changes made in code:

  1. Use TransferMode = TransferMode.StreamedResponse, instead of Streamed. I applied that only to client configuration. Server still has Streamed.
  2. Add default (parameterless) constructor to FileDownloadReturnMessage. It is necessary for MessageContract deserialization. Code - public FileDownloadReturnMessage() { }

Funny thing that now I cannot reproduce error back and everything works fine even with TransferMode.Streamed

Let me know if that helped.

I can publish my code if you still cannot fix the issue.

Interesting thing is that 400 error is generated by WCF in client. Server always returns 200 and correct data if you check traffic in fiddler.



回答2:

I think you can't transfer in the message body a Stream and another data. You need to use a MessageContract to add custom data in the header of the message, and the stream in the body.

You need to use a specific contract. You can use class with messagecontract which encapsulate your current contract. Like this :

[ServiceContract]
public interface IFileDownloadService
{
    [OperationContract]
    StreamResponse DownloadFile(DownloadFileCommandRequest command);
}

[MessageContract]
public class StreamResponse
{
    [MessageHeader()]
    public CommandResult<FileDownloadDTO> {get; set;}

    [MessageBodyMember(Order = 1)]
    public Stream FileByteStream { get; set; }
}

[MessageContract]
public class DownloadFileCommandRequest 
{
    [MessageBodyMember(Order = 1)]
    public DownloadFileCommand FileCommand {get; set;}
}

And remove the stream property in FileDownloadDTO class. Maybe if you only remove the DataMember attribute it's ok.

[DataContract]
public class FileDownloadDTO : IDisposable, IDTOBase
{
   [DataMember]
   public string FileName { get; set; }

   [DataMember]
   public long Length { get; set; }

   [DataMember]
   public int Id { get; set; }
}

Here, an exemple of the client configuration.

<binding name="FileHttpBinding"  closeTimeout="04:01:00"
                openTimeout="04:01:00" receiveTimeout="04:10:00" sendTimeout="04:01:00"
                allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"
                transferMode="Streamed" messageEncoding="Mtom">
                <readerQuotas maxDepth="128" maxStringContentLength="2147483647" maxArrayLength="2147483647"
                        maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
                <security mode="None">
                </security>
            </binding>


回答3:

Please check your connection string at service side. If you are using windows authentication in connection string then try to use SQL authentication by creating sql user for your database