Witam, właśnie tworzę swój WCF REST Service (ostatnio jakaś moda na to), praktycznie jest on już ukończony i działa ok. Problem mam z jego "używaniem". Ogólnie kontakt z WCF REST tworzę przy pomocy HttpWebRequest / HttpWebResponse, co jest dość... oczywiste :). Jednak wszystko trwało za długo, więc postanowiłem napisać klasy, które mi ten proces uprzyjemnią.

Zacznę od dołu hierarchii, że tak to nazwę, najniższą klasą jest ServiceRequestContent, która ma przeciążony konstuktor, gdzie jednym z elementów może być podany obiekt i metoda serializacji, wygląda to mniej więcej tak :

 
    public class ServiceRequestContent
    {
        // inny nieważny kod klasy
        private Stream _stream;
     
        public Stream GetContentAsStream()
        {
            return _stream;
        }

        public ServiceRequestContent(object _Object, WebMessageFormat messageFormat)
        {
            _stream = new MemoryStream();
            Type _type = _Object.GetType();

            switch (messageFormat)
            {
                case WebMessageFormat.Xml :    
                        XmlSerializer xmlSerializer = new XmlSerializer(_type);
                        XmlTextWriter xmlWriter = new XmlTextWriter(_stream, Encoding.UTF8);

                        xmlSerializer.Serialize(xmlWriter, _Object);
                        
                        this.ContentType = "application/xml";
                    break;

                case WebMessageFormat.Json :
                        DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(_type);
                        jsonSerializer.WriteObject(_stream, _Object);

                        this.ContentType = "application/json";
                    break;
            }
        }

Klasa ServiceRequestContent znajduje się w mojej klasie RequestMessage, ale tam nie dzieje się nic specjalnego. "Najwyższą" klasą jest ServiceClient, który udostępnia przeciążone metody GET/POST itd, skupmy się na POST.

 
    public class ServiceClient
    {
        public ServiceResponse Post(RequestMessage message)
        {
            //przypisanie message do request
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(message.Address);
            request.Headers = message.Headers;
            request.ContentType = message.ContentType;
            request.ContentLength = message.Content.ContentLength;
            request.Method = "POST";
            

            Stream messageStream = message.Content.GetContentAsStream();


            Stream stream = request.GetRequestStream();
            messageStream.CopyTo(stream);

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            return new ServiceResponse(response);
        }

I tutaj następuje wyjątek a dokładnie przy wywołaniu HttpWebResponse response = (HttpWebResponse)request.GetResponse();, wyjątek jest następujący.

 
ProtocolViolationException
You must write ContentLength bytes to the request stream before calling [Begin]GetResponse.

Ok, kod jest "niezgodny" z tym co można znaleźć "w internecie", oraz na msdn, po CopyTo, brakuje stream.Close(), jednak gdy go dopiszę otrzymuję na tym właśnie stream.Close() inny wyjątek :

 
WebException
The request was aborted: The request was canceled.

Jak to mówią z deszczu pod rynnę. Szukałem troszkę i wiem, że dużo osób miało z tym problem :/ Jednak w większości przypadków działał sposób z msdn, czyli z zamykaniem strumienia.

Testowałem wiele możliwości, próbowałem wywoływać na przemian zarówno na messageStream i stream takie metody jak Close(), Flush(), kombinowałem (bo gdzieś to wyczytałem) z request.KeepAlive.

Próbowałem również message.Content.ContentLength przypisać do zmiennej, następnie ustawić request.ContentLength, a później jeszcze raz contentLength przekazać jako parametr do metody CopyTo.

Sprawdzałem również kopiowanie bajt bo bajcie zamiast CopyTo i pewnie jakieś inne kombinacje, jednakże zawsze jeden z dwóch błądów :/