Get/Upload File in RESTful service

Get is simple, zero configuration rocks

This post explained how to get image from RESTful service very clearly, a nice thing of setup RESTful WCF service is no setting in web.config, so-called zero configuration.

Adding parameter is simple, just use UriTemplate.


        [OperationContract]
        [WebGet(UriTemplate = "image/{id}", RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Bare)]
        Stream GetImage(string id);

Upload is tricky, override WebServiceHostFactory to increase MaxMessageSize for posting

Stream operation is fun! Question, do I really need to set ContentType for post?

400 bad request error means file is too large, default is 32k, needs to download RESTful starter kit to override WebServiceHostFactory to return customized WebServiceHost2.

Watch out the ture parameter in the middle of ctor, ignoring it will cause non-sense error message like this:

Service ‘RuntimeType’ implements multiple ServiceContract types, and no endpoints are defined in the configuration file. WebServiceHost can set up default endpoints, but only if the service implements only a single ServiceContract. Either change the service to only implement a single ServiceContract, or else define endpoints for the service explicitly in the configuration file.


 [Test]
 public void test_upload_image_using_file_stream()
 {
 string uri = "http://localhost/FileStorageService/FileGetService.svc/upload_file/2.jpg";
 var req = WebRequest.Create(uri) as HttpWebRequest;
 if (req != null)
 {
 req.Method = "POST";
//                req.ContentType = "application/pdf";  // optional?
 Stream reqStream = req.GetRequestStream();

 var pdfStream = new FileStream(@"c:\tmp\datasource_of_datagridview.JPG", FileMode.Open, FileAccess.Read);

 pdfStream.CopyTo(reqStream);

 reqStream.Close();

 var resp = (HttpWebResponse) req.GetResponse();
 var r = new XmlTextReader(resp.GetResponseStream());
 if (r.Read())
 {
 Console.WriteLine(r.ReadString());
 }
 }
 }

 /// <summary>
 /// To increase the default post message size limit from 32656.
 /// Must change svc from Factory="System.ServiceModel.Activation.WebServiceHostFactory" to Factory="FileStorageService.CustomWebServer"
 /// </summary>
 public class CustomWebServer : WebServiceHostFactory
 {
 private static readonly long MAX_MESSAGE_SIZE = 2048576;

 protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAdresses)
 {
 WebServiceHost2 result = new WebServiceHost2(serviceType, true, baseAdresses);
 result.MaxMessageSize = MAX_MESSAGE_SIZE;
 return result;
 }
 }

Classic WCF is a little bit different, passing bytes array instead of stream needs to increase MaxArrayLength

For people do not want to deal with web request/response, there is an non-RESTful file upload solution using classic WCF service, also works very well, while it is using byte type to pass file contents to server. I have tried to switch to stream but got error said

“[For request in operation UploadFile to be a stream the operation must have a single parameter whose type is Stream.]”

Solution can be found here.

Debugging in silverlight is realy painful, unfriendly 404 error hides too many info away!

Advertisements

easy upgrade WCF Service to RESTful

Why?

We are in kind of dilemma of choosing the architecture for our upcoming web applications. MVC or SL4? Because the first few pages we are going to build are quite simple, so I prefer to make it on REST+jQueryUI for now. It should be very easy to switch to either one in the future.

We already have the WCF service built, the only challenge is to make it RESTful.

How?

WebGet

Add webGet attribute/verb on Contract. Default response type is xml.

[ServiceContract]
public interface ICountryProvinceWCFService
{

    [OperationContract]
    [WebGet(ResponseFormat=WebMessageFormat.Json)]
    string[] GetProvinceGET(string Country);
}

behaviorConfiguration

According to ms help WegGet is useless until web http behavior is defined in web.config.

Note: this is a endpoint behavior, not service behavior, this must be set on endpoint level.

Also, binding type needs to be webHttpBinding.

    <services>
      <service behaviorConfiguration="DefaultServicesBehavior" name="MyWcfService">
        <endpoint address="" binding="wsHttpBinding" contract="IMyWcfService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <endpoint address="rest" binding="webHttpBinding" contract="IMyWcfService" behaviorConfiguration="RestfulWebServiceEndpointBehavior" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost/RestDemo" />
          </baseAddresses>
        </host>
      </service>

...
    <behaviors>
      <endpointBehaviors>
        <behavior name="RestfulWebServiceEndpointBehavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>

    </behaviors>

ref:

http://www.codeproject.com/KB/aspnet/WCF_JQUERY_ASMX.aspx