I need create WCF REST service for uploading large files. I made endpoint as streamed webHttpBinding, but it have not became streamed.
Service example:
[ServiceContract]
public interface IFiles
{
[OperationContract]
void UploadFile(Stream stream);
}
public class Files : IFiles
{
public void UploadFile(Stream stream)
{
const int BUFFER_SIZE = 64 * 1024;
byte[] buffer = new byte[BUFFER_SIZE];
using (TextWriter logWriter = new StreamWriter("d:\\UploadedFile.log"))
using (Stream fileStream = new FileStream("d:\\UploadedFile", System.IO.FileMode.Create, System.IO.FileAccess.Write))
{
int readBytes = 0;
while (0 < (readBytes = stream.Read(buffer, 0, BUFFER_SIZE)))
{
fileStream.Write(buffer, 0, readBytes);
logWriter.WriteLine("{0}: {1} bytes saved", DateTime.Now, readBytes);
}
}
}
}
Web.config:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="WebHttpBinding" maxBufferSize="65536" maxBufferPoolSize="524288"
maxReceivedMessageSize="1073741824" transferMode="Streamed" />
</webHttpBinding>
</bindings>
<services>
<service name="WcfService2.Files">
<endpoint behaviorConfiguration="WebHttpBehavior" binding="webHttpBinding"
bindingConfiguration="WebHttpBinding" name="Files" contract="WcfService2.IFiles" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="WebHttpBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata />
<serviceDebug />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.web>
<httpRuntime maxRequestLength="500000" />
</system.web>
</configuration>
Client code:
using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlString);
request.Method = "POST";
request.ContentType = "application/octet-stream";
//request.ContentLength = fileStream.Length;
//request.AllowWriteStreamBuffering = false;
request.SendChunked = true;
Stream requestStream = request.GetRequestStream();
const int BUFFER_SIZE = 32 * 1024;
byte[] buffer = new byte[BUFFER_SIZE];
int readBytes = 0;
while (0 < (readBytes = fileStream.Read(buffer, 0, BUFFER_SIZE)))
{
requestStream.Write(buffer, 0, readBytes);
Console.WriteLine("{0}: {1} bytes sent", DateTime.Now, readBytes);
System.Threading.Thread.Sleep(200);
}
requestStream.Close();
WebResponse response = request.GetResponse();
}
Code of method UploadFile is invoked only after requestStream.Close() is invoked. Why?
If you want to send it in parts you should add a flush in the while loop after the requestStream.Write()
requestStream.Flush();
This because streams may cache the output till it's nessecary to send it. See http://msdn.microsoft.com/en-us/library/system.io.stream.flush.aspx
Related
I'm currently working on a WCF streaming service. So far everything works great for files up to 2 GB. I've set up the service as a streaming service and I am chunking the files on my own on 5 MB chunks. However, files bigger than 2 GB (somewhere there is the threshold) i always get an InvalidOperationExceptionwith the message Timeouts are not supported on this stream. I'm not really sure why and where this exception is thrown. It don't think this is a server side problem because every request should be the same and most of them work. But the exceptions comes from the generated proxy. So the source is System.Private.ServiceModel
Stack trace:
at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass0.<CreateGenericTask>b__1(IAsyncResult asyncResult)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Company.OurApp.App.DataService.BaseFile.<DownloadItem>d__59.MoveNext()
Here is my server implementaion:
var response = new GetFileResponse();
using (var impersonation = new Impersonation(request.Domain, request.Username, request.Password))
{
using (Stream fStream = File.OpenRead(request.FullFilePath))
{
fStream.Seek(request.FilePart * request.FilePartSize, SeekOrigin.Begin);
BinaryReader bStream = new BinaryReader(fStream);
var filePart = bStream.ReadBytes(request.FilePartSize);
using (Stream mStream = new MemoryStream(filePart))
{
response.FileByteStream = mStream;
return response;
}
}
}
The GetFileResponse looks like this:
[MessageContract]
public class GetFileResponse
{
[MessageBodyMember(Order = 1)]
public Stream FileByteStream { get; set; }
}
This is how the client handles the download (UWP App):
using (Stream f = await StorageFile.OpenStreamForWriteAsync())
{
//Cancelation area - after every async operation if possilble
for (int i = 0; i < sections; i++)
{
token.ThrowIfCancellationRequested();
var response = await client.GetFilePartAsync(request.ConnectionPassword, request.Domain, i, FilePartSize, FullPath, request.Password, request.Username);
token.ThrowIfCancellationRequested();
DownloadProgress = response.FileByteStream.Length;
f.Seek(0, SeekOrigin.End);
await f.WriteAsync(response.FileByteStream, 0, response.FileByteStream.Length);
await f.FlushAsync();
}
}
And here is the service web.config:
<system.serviceModel>
<services>
<service behaviorConfiguration="HttpsServiceBehaviour"
name="Company.OurApp.TransportService.DataService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="streamedBinding" contract="Company.OurAppTransportService.IDataService">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="HttpsServiceBehaviour">
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="streamedBinding" transferMode="Streamed" closeTimeout="10:00:00">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
When generating the client proxy, i set some timeouts but that didn't change anything:
public DataServiceClient GetDataServiceClient(string endpoint = null)
{
var useEndpoint = String.IsNullOrEmpty(endpoint) ? Configuration.ConfigService : endpoint;
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.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows;
//TODO Try to work with timeouts for larges files?
result.SendTimeout = TimeSpan.FromMinutes(5);
result.ReceiveTimeout = TimeSpan.FromMinutes(5);
result.OpenTimeout = TimeSpan.MaxValue;
if (useEndpoint.ToLower().StartsWith("https://"))
result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
else
result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.TransportCredentialOnly;
var client = new DataServiceClient(result, new System.ServiceModel.EndpointAddress(String.Concat(useEndpoint, fixedEndpointSuffix)));
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
if (AppState.IsLoggedIn)
{
client.ClientCredentials.Windows.ClientCredential.UserName = $#"{AppState.Domain}\{AppState.User}";
client.ClientCredentials.Windows.ClientCredential.Password = AppState.Password;
}
return client;
}
Any idea where and why the exception is thrown? Server? Client? Is it coming from the stream? Help is very much appreciated.
For everybody else facing the same problem. I got it solved by analyzing the exceptions with the WCF TraceViewer. I also called the Service from a console application to be sure it's not a UWP problem. The problem was that I closed the streams before the response could reach the client.
Broken implementation:
var response = new GetFileResponse();
using (var impersonation = new Impersonation(request.Domain, request.Username, request.Password))
{
using (Stream fStream = File.OpenRead(request.FullFilePath))
{
fStream.Seek(request.FilePart * request.FilePartSize, SeekOrigin.Begin);
BinaryReader bStream = new BinaryReader(fStream);
var filePart = bStream.ReadBytes(request.FilePartSize);
using (Stream mStream = new MemoryStream(filePart))
{
response.FileByteStream = mStream;
return response;
}
}
}
This one fixed it for me:
Stream fStream = File.OpenRead(request.FullFilePath);
long offset = request.FilePart * request.FilePartSize;
fStream.Seek(offset, SeekOrigin.Begin);
BinaryReader bStream = new BinaryReader(fStream);
var filePart = bStream.ReadBytes((int)request.FilePartSize);
Stream mStream = new MemoryStream(filePart);
response.FileByteStream = mStream;
return response;
Hope it helps!
I was trying to work on the following requirement for days. I would like my WCF API to handle XML and JSON requests/responses using a single method. I also know that .net 4 supports automaticformat selection but it does not work the way i want my XML & JSON structures to be. Here are my structures:
JSON:
{
"response": {
"timestamp": "12.00AM",
"locations": {
"location": [
{
"id": "5",
"name": "hello world",
"statusid": "8"
}
]
},
"errorcode": "444"
}
}
XML:
<response>
<timestamp>12.00AM</timestamp>
<locations>
<location>
<id>5</id>
<name>hello world</name>
<statusid>8</statusid>
</location>
</locations>
<errorcode>444</errorcode>
</response>
I have tried toggling the value of "BodyStyle" in my OperationContract, e.g. for JSON i have to put WebMessageBodyStyle.Wrapped and for XML i have to put WebMessageBodyStyle.Bare to work according to the above structures. Since i want to be able to use one OperationContract and automatically respond with XML/JSON structures depending on the Content-Type, what changes/additions do i have to make? Is there a way to set this BodyStyle using Code for both XML and JSON (P.S. API should be the way it is and should not be able pass any parameters like getvalue/{xml})?
Thank you in advance.
UPDATE: below is my OperationContract:
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "PushNotify")]
ResponsePushNotify PushNotify(RequestPushNotifiy pushnotify);
Here is the DataContract:
[DataContract]
public class Test: ITest
{
responsePushNotify = new ResponsePushNotify();
ResponsePushNotify PushNotify(RequestPushNotifiy pushnotify)
{
if (Content-Type == "application/json; charset=utf-8")
{
OperationContext.Current.OutgoingMessageProperties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Json));
}
responsePushNotify.id = "1";
responsePushNotify.value = "Hello World";
return responsePushNotify ;
}
Here's the suggested code:
public class MyWebHttpBehavior : WebHttpBehavior
{
protected override IDispatchMessageFormatter GetReplyDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
WebGetAttribute webGet = operationDescription.Behaviors.Find<WebGetAttribute>();
IDispatchMessageFormatter json = null, xml = null;
WebMessageFormat originalFormat = webGet.ResponseFormat;
webGet.ResponseFormat = WebMessageFormat.Json;
json = base.GetReplyDispatchFormatter(operationDescription, endpoint);
webGet.ResponseFormat = WebMessageFormat.Xml;
xml = base.GetReplyDispatchFormatter(operationDescription, endpoint);
webGet.ResponseFormat = originalFormat;
return new MyReplyDispatchMessageFormatter(json, xml);
}
}
public class MyReplyDispatchMessageFormatter : IDispatchMessageFormatter
{
IDispatchMessageFormatter jsonFormatter;
IDispatchMessageFormatter xmlFormatter;
public MyReplyDispatchMessageFormatter(IDispatchMessageFormatter jsonFormatter, IDispatchMessageFormatter xmlFormatter)
{
this.jsonFormatter = jsonFormatter;
this.xmlFormatter = xmlFormatter;
}
public void DeserializeRequest(Message message, object[] parameters)
{
throw new NotSupportedException("Used for replies only");
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
IDispatchMessageFormatter formatter = this.xmlFormatter;
if (OperationContext.Current.OutgoingMessageProperties.ContainsKey(WebBodyFormatMessageProperty.Name))
{
WebBodyFormatMessageProperty webBody = (WebBodyFormatMessageProperty)OperationContext.Current.OutgoingMessageProperties[WebBodyFormatMessageProperty.Name];
if (webBody != null && webBody.Format == WebContentFormat.Json)
{
formatter = this.jsonFormatter;
}
}
return formatter.SerializeReply(messageVersion, parameters, result);
}
}
Here's my webconfig:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
<services>
<service behaviorConfiguration="CustomBehavior" name="Service.Test">
<endpoint address="" behaviorConfiguration="web" binding="webHttpBinding" contract="Services.ITest" bindingConfiguration="general"/>
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="general" closeTimeout="00:10:00" openTimeout="00:10:00" sendTimeout="00:10:00"
receiveTimeout="00:10:00" maxReceivedMessageSize="2147483647" maxBufferPoolSize="4194304"
maxBufferSize="2147483647" />
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp automaticFormatSelectionEnabled="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="CustomBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
So now my problem is, how do i define my custom behavior in code and add it to webconfig? What changes do i need to do? Also if i were to set "BodyStyle = WebMessageBodyStyle.Wrapped" How & Where do i do that?
I see that this question posted very long time ago but may be this will be useful for somebody. I found solution on overcoder.net. All you need is to generate correspondent strings for desired output format and use Stream for result type.
[OperationContract]
[WebGet(UriTemplate = "test?format={format}")]
System.IO.Stream test(string format);
public System.IO.Stream test(string format)
{
ArrayList ar = new ArrayList() { 1, 2, 3 };
if (format == "text")
result = String.Join(",", ar.ToArray());
if (format == "xml")
{
result = #"<soapenv:Envelope>
<soapenv:Header/>
<soapenv:Body>
<result>";
foreach (int i in ar)
result += String.Format("\r\n <item>{0}</item>", i);
result += #"
</result>
</soapenv:Body>
</soapenv:Envelope>";
}
if (format == "json")
result = JsonConvert.SerializeObject(ar, Newtonsoft.Json.Formatting.Indented);
OutgoingWebResponseContext context = WebOperationContext.Current.OutgoingResponse;
context.ContentType = "text/plain";
return new System.IO.MemoryStream(ASCIIEncoding.Default.GetBytes(result));
}
I am creating a WCF Rest Service to Upload Images from Mobile application. but i am getting
The remote server returned an error: (400) Bad Request. can any one point me what i have done wrong.
Following are my Definitions :
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "/PostImage",Method ="POST")]
PublicMessage PostImage(Upload obj);
[DataContract]
public class Upload
{
[DataMember]
public Stream File { get; set; }
}
Service Definition :
public PublicMessage PostImage(Upload obj)
{
byte[] buffer = StreamToByte(obj.File); //Function to convert the stream to byte array
FileStream fs = new FileStream(#"D:\ShopMonkeyApp\Desert.jpg", FileMode.Create, FileAccess.ReadWrite);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(buffer);
bw.Close();
return new PublicMessage { Message = "Recieved the image on server" };
}
Client Application :
string filePath = #"D:\ShopMonkeyApp\Desert.jpg";
string url = "http://localhost:50268/shopmonkey.svc/PostImage/"; // Service Hosted in IIS
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "text/xml";
request.Method = "POST";
request.ContentType = "image/jpeg";
using (Stream fileStream = File.OpenRead(filePath))
using (Stream requestStream = request.GetRequestStream())
{
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int byteCount = 0;
while ((byteCount = fileStream.Read(buffer, 0, bufferSize)) > 0)
{
requestStream.Write(buffer, 0, byteCount);
}
}
string result;
using (WebResponse response = request.GetResponse())
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
}
Console.WriteLine(result);
Web Config :
<system.serviceModel>
<services>
<service name="ShopMonkey.ShopMonkey" behaviorConfiguration="ServiceBehaviour">
<!-- Service Endpoints -->
<!-- Unless fully qualified, address is relative to base address supplied above -->
<endpoint address ="" binding="webHttpBinding" contract="ShopMonkey.IShopMonkey" behaviorConfiguration="web">
<!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.behaviorConfiguration="web"
-->
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp/>
<dataContractSerializer maxItemsInObjectGraph="10000000"/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
Thanks
Vijay
Increasing the message queue length in web.config solves my issue.
<webHttpBinding>
<binding name="streamWebHttpbinding" transferMode="Streamed" maxReceivedMessageSize="1000000000000" receiveTimeout="01:00:00" sendTimeout="01:00:00" />
</webHttpBinding>
Thanks to all
If you dont have any other properties on the Upload class then change the WCF service method to have a Stream param rather than wrapping it in a class as below:
[OperationContract]
[WebInvoke(UriTemplate = "/PostImage",Method ="POST")]
PublicMessage PostImage(Stream obj);
Then you can use the WebClient class to upload a file directly as shown below:
var c = new System.Net.WebClient();
c.OpenWrite(string.Concat("http://localhost:50268/shopmonkey.svc", "/PostImage"), "POST");
c.Headers[HttpRequestHeader.ContentType] = "image/jpeg";
return c.UploadFile(string.Concat(serviceBaseUrl, resourceUrl), filePath);
Also refer to this link.
UPDATE :
Please find the sample to get your code working below:
[OperationContract]
[WebInvoke(UriTemplate = "/PostImage",Method ="POST")]
PublicMessage PostImage(Upload obj);
[DataContract]
public class Upload
{
[DataMember]
public MemoryStream FileContent { get; set; }
}
Now your method that implements the PostImage looks as follows:
public PublicMessage PostImage(Upload obj)
{
byte[] buffer = new byte[obj.FileContent.Length];
using (FileStream ms = new FileStream(#"D:\ShopMonkeyApp\Temp\Desert.jpg", FileMode.OpenOrCreate))
{
obj.FileContent.Position = 0;
int read = fileInfo.Content.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, read);
}
return new PublicMessage { Message = "Recieved the image on server" };
}
Now since our server side code is complete now moving to the client side part that uploads your file to the server as below:
private string ClientSample()
{
var uploadObject = new Upload();
Image image = Image.FromFile(#"D:\ShopMonkeyApp\Desert.jpg");
MemoryStream ms = new MemoryStream();
uploadObject.FileContent = new MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.WriteTo(uploadObject.FileContent);
ms.Close();
string responseMessage = null;
var request = WebRequest.Create(http://localhost:50268/shopmonkey.svc/PostImage) as HttpWebRequest;
if (request != null)
{
request.ContentType = "application/xml";
request.Method = method;
}
if(method == "POST" && requestBody != null)
{
byte[] requestBodyBytes;
requestBodyBytes = ToByteArrayUsingDataContractSer<Upload>(requestBody);
request.ContentLength = requestBodyBytes.Length;
using (Stream postStream = request.GetRequestStream())
postStream.Write(requestBodyBytes, 0, requestBodyBytes.Length);
}
if (request != null)
{
var response = request.GetResponse() as HttpWebResponse;
if(response.StatusCode == HttpStatusCode.OK)
{
Stream responseStream = response.GetResponseStream();
if (responseStream != null)
{
var reader = new StreamReader(responseStream);
responseMessage = reader.ReadToEnd();
}
}
else
{
responseMessage = response.StatusDescription;
}
}
}
private static byte[] ToByteArrayUsingDataContractSer<T>(T requestBody)
{
byte[] bytes = null;
var serializer1 = new DataContractSerializer(typeof(T));
var ms1 = new MemoryStream();
serializer1.WriteObject(ms1, requestBody);
ms1.Position = 0;
var reader = new StreamReader(ms1);
bytes = ms1.ToArray();
return bytes;
}
NOTE: Make sure that your Upload object both on the client and server have the same namespace and properties defined so that you avoid any deserialization issues.
I want to set message version for WSHttpBinding to EnvelopeVersion.Soap11. I don't know how to do that. Can any one help me. Here is my binding code
var binding = new WSHttpBinding()
{
UseDefaultWebProxy = true,
Security =
{
Mode = SecurityMode.Transport,
Transport =
{
ClientCredentialType = HttpClientCredentialType.Basic
},
},
};
EDIT:
here is the code to do that
TransportBindingElement transportElement = null;
transportElement = new HttpsTransportBindingElement();
((HttpsTransportBindingElement)transportElement).AuthenticationScheme = AuthenticationSchemes.Basic;
((HttpsTransportBindingElement) transportElement).KeepAliveEnabled = false;
var messegeElement = new TextMessageEncodingBindingElement
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap11, AddressingVersion.None),
ReaderQuotas =
{
MaxArrayLength = 200000,
MaxBytesPerRead = 200000,
MaxDepth = 200000,
MaxNameTableCharCount = 200000,
MaxStringContentLength = 200000
}
};
var binding = new CustomBinding(messegeElement, transportElement);
return binding;
In order to achieve this, you need to define a custom binding - in config or in code.
In config, you'd do it something like this:
<system.serviceModel>
<bindings>
<customBinding>
<binding name="Soap11">
<textMessageEncoding messageVersion="Soap11" />
<httpTransport />
</binding>
</customBinding>
</bindings>
and then, in your service or client config, use something like this:
<services>
<service name="test">
<endpoint name="TestEP"
address=""
binding="customBinding"
bindingConfiguration="Soap11"
contract="IService" />
</service>
</services>
</system.serviceModel>
I've been trying to get data from a wcf service and into the fullcalendar control. However, ive had no luck and wondered what i was doing wrong.
jscript :
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,basicWeek,basicDay'
},
editable: false,
height: 200,
aspectRatio: 100,
events: "http://localhost:63295/_services/Service2.svc/DoWork/"
etc...
WCF interface :
[ServiceContract]
public interface IService2
{
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json)]
string[] DoWork();
}
WCF Service :
public string[] DoWork()
{
// Add your operation implementation here
SortedDictionary<string, string> d = new SortedDictionary<string, string>();
NameValueCollection AE = new NameValueCollection();
SqlDataReader sdr = ReadData("SelectALLAE");
while (sdr.Read())
{
AE.Add("title", sdr["AE_EmployeeID"].ToString() + " " + sdr["AE_EmployeeName"].ToString() + " " + sdr["AE_EventCode"].ToString());
AE.Add("start", sdr["AE_StartDateTime"].ToString());
AE.Add("end", sdr["AE_EndDateTime"].ToString());
}
return AE.GetValues(0).ToArray();
}
Web.config :
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="CountryProvinceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="CountryProvinceBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="CountryProvinceBehavior" name="TimesheetsV2._0_Investigations._services.Service2">
<endpoint address="" binding="webHttpBinding" contract="TimesheetsV2._0_Investigations._services.IService2" behaviorConfiguration="CountryProvinceBehavior"/>
</service>
</services>
</system.serviceModel>
I've successfully connected to this wcf on a page without the full calendar. This was so i could test how to connect to the wcf service via jquery.
But when i use the fullcalendar event option, nothing happens. it doesn't even connect the wcf service at all ( i tried to do a debug on the service and nothing happened ).
any help would be appreciated
thanks
I know this is 7 months old but - try taking the trailing slash off the url call in the $.ajax call.
I think you want : "http://localhost:63295/_services/Service2.svc/DoWork"
try this:
$("#calendar").fullCalendar({
events: {
url: 'http://localhost:63295/_services/Service2.svc/DoWork',
type: 'POST',
data: '{}',
error: function () {
alert('there was an error while fetching events!');
},
color: 'yellow', // a non-ajax option
textColor: 'black' // a non-ajax option
}
});
Also - consider changing the bodystyle of your webinvoke call BodyStyle parameter to BodyStyle = WebMessageBodyStyle.WrappedRequest (I've read you'll get an error with plain wrapped). And not sure if you're doing it already, but be sure to code your start and end values in your list / array as unix time stamps or one of the allowed date types.
your web.config is good.
Here's something I've tested (.net 4.0) with the above fullcalendar ajax call except I used a list not an array - I'm presuming you may be reading in standard dates which won't work either, so there's a function here to convert the dates to unix timestamps on the fly, just dodge it if you're already working with unix time stamps.
[WebInvoke(Method="POST", BodyStyle=WebMessageBodyStyle.WrappedRequest, ResponseFormat=WebMessageFormat.Json)]
[OperationContract]
public List<AELIST> DoWork()
{
SqlConnection sqlconn = new SqlConnection();
sqlconn.ConnectionString = ConfigurationManager.ConnectionStrings["YourConnNameInWebConfig"].ConnectionString;
sqlconn.Open();
string sqlstring = "Select * from YourTable";
SqlCommand sqlcomm = new SqlCommand(sqlstring, sqlconn);
SqlDataReader sreader = sqlcomm.ExecuteReader();
List<AELIST> AE = new List<AELIST>();
while (sdr.Read())
{
DateTime dsx = Convert.ToDateTime(sdr["AE_StartTime"]);
Double mystart = ConvertToTimestamp(dsx);
DateTime dex = Convert.ToDateTime(sdr["AE_EndDateTime"]);
Double myend = ConvertToTimestamp(dex);
AELIST AEEntry = new AELIST<>();
AEEntry.title = sdr["AE_EmployeeID"].ToString() + " " + sdr["AE_EmployeeName"].ToString() + " " + sdr["AE_EventCode"].ToString();
AEEntry.start = mystart.ToString();
AEEntry.end = myend.ToString();
AE.Add(AEEntry);
}
sqlconn.Close();
sdr.Close();
sreader.Dispose();
sqlcomm.Dispose();
return AE;
}
private double ConvertToTimestamp(DateTime value)
{
TimeSpan span = (value - new DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime());
return (double)span.TotalSeconds;
}
public class AELIST
{
public string title {get; set;}
public string start {get; set;}
public string end {get; set;}
}