Consuming SF 4 REST service

Posted by Community Admin on 05-Aug-2018 13:55

Consuming SF 4 REST service

All Replies

Posted by Community Admin on 08-Sep-2010 00:00

Hi,

I’m having a hard time consuming the SF4 webservices.

 

Could you please be so kind to provide a code snippet that connects to “services/pages/PagesService.svc” and retrievs a “CollectionContext<PageViewModel>” from the methode “GetPagesAsTree”.

 

I do receive a 405 error. I’m also not quite sure how to authenticate this request with the right user credentials.

A code-snippet would be very helpful as I’m stuck with this basic step..:(

 

What I’m trying to do: Connect to SF4 webservices from an .net windows-forms application which will be used as a migration tool for our old CMS.

Thanx

Posted by Community Admin on 08-Sep-2010 00:00

Hello SolarX,

GetPagesAsTree uses POST method to retrieve the data. GetPagesAsTree returns xml or jason that you should parse. You can use /PagesService.svc/xml/ which will return the data as  XML. You have to make web request where the method type is POST.(WebRequest.Create)

By default the Get method is processed without authentication when you are not trying to create, delete or update an item.. Please check whether you make the proper POSt request. You can use GetPages which also returns all pages and parent IDs so you can make the relation easily.

Best wishes,
Ivan Dimitrov
the Telerik team


Check out Telerik Trainer, the state of the art learning tool for Telerik products.

Posted by Community Admin on 09-Sep-2010 00:00

Thank you for your quick answer.
Here is the code I'm using in a test project. When I call GetPagesAsTree at the end I get an "405 method not allowed" exception. Maybe my usage of the parameters is wrong.

private void CallService()
       
            BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None);
            EndpointAddress address = new EndpointAddress("ffm-srv-ext-2a/.../");

            ChannelFactory<IPagesService> factory =
                new ChannelFactory<IPagesService>(binding, address);
            factory.Open();

            IPagesService channel = factory.CreateChannel();

            string[] leafIds = new string[10];
            CollectionContext<PageViewModel> pageTree = channel.GetPagesAsTree(leafIds, "", 10, 10, 10, 10, "");

            factory.Close();
       

Posted by Community Admin on 09-Sep-2010 00:00

Hello SolarX,

If you are using IIS 7.5 make sure to disable the WebDAV module. It restricting put and delete methods of webservices. You can check this article for more information.

Regards,
Radoslav Georgiev
the Telerik team


Check out Telerik Trainer, the state of the art learning tool for Telerik products.

Posted by Community Admin on 09-Sep-2010 00:00

Hi,

I've changed the web.config accordingly. Anyway WEBDav feature isn't installed on the server at all.
Still getting the same error.

Thanx for your help!

Posted by Community Admin on 09-Sep-2010 00:00

Hi SolarX,

The problem is not coming from the parameters used. It is coming from the HttpBinding used. Try this :

WebHttpBinding binding = new WebHttpBinding();
EndpointAddress address = new EndpointAddress("http://localhost:4000/Sitefinity/services/pages/PagesService.svc/");
ChannelFactory<IPagesService> factory =
    new ChannelFactory<IPagesService>(binding, address);
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
factory.Open();
IPagesService channel = factory.CreateChannel();
WcfPageContext page = channel.GetPage("35bb4ae9-d916-49f7-80db-cf8823746105", null, false);

Or bellow is an alternative approach you can use to call the services:
var request1 = (HttpWebRequest)WebRequest.Create("http://localhost:4000/Sitefinity/Services/Pages/PagesService.svc/tree/");
        request1.Method = "POST";
        string postData = @"[""365af67c-a4a5-4552-83d9-e5ceb2dcbc06""]";
        byte[] byteArray = Encoding.UTF8.GetBytes(postData);
        // Set the ContentType property of the WebRequest.
        request1.ContentType = "application/json";
        // Set the ContentLength property of the WebRequest.
        request1.ContentLength = byteArray.Length;
        // Get the request stream.
        Stream dataStream = request1.GetRequestStream();
        // Write the data to the request stream.
        dataStream.Write(byteArray, 0, byteArray.Length);
        // Close the Stream object.
        dataStream.Close();
        // Get the response.
        WebResponse response1 = request1.GetResponse();
        // Display the status.
        Console.WriteLine(((HttpWebResponse)response1).StatusDescription);
        // Get the stream containing content returned by the server.
        dataStream = response1.GetResponseStream();
        // Open the stream using a StreamReader for easy access.
        StreamReader reader = new StreamReader(dataStream);
        // Read the content.
        string responseFromServer = reader.ReadToEnd();
        // Display the content.
        Console.WriteLine(responseFromServer);
        // Clean up the streams.
        reader.Close();
        dataStream.Close();
        response1.Close();

Regards,
Radoslav Georgiev
the Telerik team

Check out Telerik Trainer, the state of the art learning tool for Telerik products.

Posted by Community Admin on 09-Sep-2010 00:00

Great. That solved the 405 error !! Thanx :)

I do now get an "Internal Server Error" exception when calling "GetPagesAsTree". From the server log I can see it should be a problem with the parameters I supply. Could you please provide further information about the required parameters.

Thank you very much !!

This is from the server app log:

Exception information:

Exception type: ArgumentNullException

Exception message: Value cannot be null.

Parameter name: g

 

Request information:

Request URL: ffm-srv-ext-2a/.../

Request path: /SF4B/Sitefinity/services/pages/PagesService.svc/xml/tree/

User host address: 10.73.11.73

User: Anonymous

Is authenticated: False

Authentication Type: Sitefinity

Thread account name: IIS APPPOOL\DefaultAppPool

 

Thread information:

Thread ID: 5

Thread account name: IIS APPPOOL\DefaultAppPool

Is impersonating: False

Stack trace: at System.Guid..ctor(String g)

at Telerik.Sitefinity.Modules.Pages.Web.Services.PagesService.GetPagesAsTreeInternal(String[] leafIds, String provider, Int32 nodesLimit, Int32 perLevelLimit, Int32 perSubtreeLimit, Int32 subtreesLimit, String root)

at SyncInvokeGetPagesAsTreeInXml(Object , Object[] , Object[] )

at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)

at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)

at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)

at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)

at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

 

 

Custom event details:

Posted by Community Admin on 09-Sep-2010 00:00

Hi SolarX,

The  error is thrown, because one of the parameters you pass is not correct or null( but it should not be a null). The stack does not contain which of your parameters is the problematic one, but I suppose that this is leafIds.

Greetings,
Ivan Dimitrov
the Telerik team


Check out Telerik Trainer, the state of the art learning tool for Telerik products.

Posted by Community Admin on 10-Sep-2010 00:00

Hi,

that is what I thought.
Since the parameters aren't documented yet, could you please provide a specific example for "GetPagesAsTree".
I really need to get further with my project. What I need is to get a hierarchical list of all pages (structure of the site). Besides the parameters, is "GetPagesAsTree" the right method to achieve that?

Thanx,

Martin

Posted by Community Admin on 10-Sep-2010 00:00

Hi SolarX,

GetPagesAsTree accepts the following parameters

leafIds - The ids of the leaf pages - if empty array an error is thrown
provider - provider that will be used - if null the default one is used
nodesLimit - limit the number of nodes  - int value / 0 gets all
perLevelLimit - limit level nodes - int value / 0 gets all
subtreesLimit - limit the sub trees  - int value  / 0 gets all
root - should be null to get all pages - type - string.


The code that we sent you is a working sample which returns the data in JSON and we get the response as stream, but you could parse it as xml or directly use GetPagesAsTreeInXml



Sincerely yours,
Ivan Dimitrov
the Telerik team


Check out Telerik Trainer, the state of the art learning tool for Telerik products.

Posted by Community Admin on 16-May-2011 00:00

What file do we add your WebHttpBinding or HttpWebRequest code to.  Can you give instructions where to create the file and what to call it.

Posted by Community Admin on 16-May-2011 00:00

Hi Dean,

It depends on from where you want to call the service - user control, custom control, page etc.
The better option is using Sys.Net.WebRequest(); to call the service.

All the best,
Ivan Dimitrov
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>

Posted by Community Admin on 16-May-2011 00:00

Ivan

If we were going to use the Sys.Net.WebRequest(); to call the service, do you have sample code and how to implement it.

With Sitefinity 4.x, are all of the webpages stored in sql?

Posted by Community Admin on 16-May-2011 00:00

Hi Dean,

This is a standard MS AJAX call.

sample

       var wRequest = new Sys.Net.WebRequest();
        wRequest.set_url(this._generateNonBinderUrl(url, urlParams, keys));
        wRequest.set_httpVerb('POST');
        this._applyLocalizationHeaders(wRequest);

        if (data)
            var postData = Sys.Serialization.JavaScriptSerializer.serialize(data);
            wRequest.set_body(postData);
            wRequest.get_headers()["Content-Type"] = "application/json";
       
        else
            alert('POST method cannot be invoked without the data');
            return;
       

        var delegates = Success: successDelegate, Failure: failureDelegate, Caller: caller, Context: context ;
        wRequest.set_userContext(delegates);
        wRequest.add_completed(this.NonBinderCallbackDelegate);
    ,

Best wishes,
Ivan Dimitrov
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>

Posted by Community Admin on 16-May-2011 00:00

Thanks again Ivan.

With Sitefinity 4.x, are all of the webpages stored in sql.  Is there a main page that we can edit to consume this code?

Posted by Community Admin on 16-May-2011 00:00

Hi Dean,

You can create a simple user control where you trigger the service call with a button.

All the best,
Ivan Dimitrov
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>

Posted by Community Admin on 18-May-2011 00:00

The information in this thread has been really helpful so far but I can't seem to find a  way to authenticate an application outside of the browser/web application to use the REST Services. Normal authentication techniques with WCF don't seem to work. Could some sort of example of this be supplied. Thanks.

Posted by Community Admin on 19-May-2011 00:00

Hello Ray,

Check if this helps. Recently we found some issues related to the logout of the users in this scenario, but we are working on the issue.

Greetings,
Ivan Dimitrov
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>

Posted by Community Admin on 11-Aug-2011 00:00

I worked up a workaround for the WCF services and authentication from an external application with regard to authenticating a user with the Sitefinity Users.svc service and then subsequently accessing resources that require an authenticated user (retrieving a page from PagesService.svc, for example).

The issue seemed to be that between WCF calls, the authorization cookie returned with the AuthenticateUser method is not stored, and thus not sent when making the next call to another service.

Based on this blog entry, http://megakemp.wordpress.com/2009/02/06/managing-shared-cookies-in-wcf/, I repurposed that project into small WCF behavior extension (almost no changes...) that stores the cookies and injects it into any of the next WCF service calls which utilize that behavior.

class PersistantCookieBehaviorExtensionElement : BehaviorExtensionElement
    protected override object CreateBehavior()
    
        return new PersistantCookieBehavior();
    
 
    public override Type BehaviorType
    
        get return typeof(PersistantCookieBehavior);
    
 
class PersistantCookieBehavior : IEndpointBehavior
    public void Validate(ServiceEndpoint endpoint)
    
        return;
    
 
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    
        return;
    
 
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    
        return;
    
 
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    
        clientRuntime.MessageInspectors.Add(PersistantCookieClientMessageInspector.Instance);
    
 
public class PersistantCookieClientMessageInspector : IClientMessageInspector
    private static PersistantCookieClientMessageInspector instance;
    private string persistedCookie;
 
    private PersistantCookieClientMessageInspector()
 
    public static PersistantCookieClientMessageInspector Instance
    
        get
        
            if (instance == null)
            
                instance = new PersistantCookieClientMessageInspector();
            
 
            return instance;
        
    
 
    public void AfterReceiveReply(ref Message reply, object correlationState)
    
        var httpResponse =
            reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
 
        if (httpResponse != null)
        
            string cookie = httpResponse.Headers[HttpResponseHeader.SetCookie];
 
            if (!string.IsNullOrEmpty(cookie))
            
                this.persistedCookie = cookie;
            
        
    
 
    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    
        if (!request.Properties.ContainsKey(HttpRequestMessageProperty.Name))
        
            request.Properties.Add(HttpRequestMessageProperty.Name, new HttpRequestMessageProperty());
        
 
        var httpRequest = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
        httpRequest.Headers.Add(HttpRequestHeader.Cookie, this.persistedCookie);
 
        return null;
    

Since I was using a WPF app, I configured the WCF behaviors and endpoints as:

<system.serviceModel>
  <extensions>
    <behaviorExtensions>
      <add name="PersistantCookieBehavior" type="MyAssembly.PersistantCookieBehaviorExtensionElement, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </behaviorExtensions>
  </extensions>
 
  <behaviors>
    <endpointBehaviors>
      <behavior name="CookieBehaviorConfig">
        <PersistantCookieBehavior />
      </behavior>
    </endpointBehaviors>
  </behaviors>
 
  <bindings>
    <webHttpBinding>
      <binding name="PagesServiceBinding" maxReceivedMessageSize="1024000000" />
    </webHttpBinding>
  </bindings>
   
  <client>
    <endpoint name="UserServiceEndpoint"
              address="http://localhost/sitefinity/services/security/Users.svc/"
              binding="webHttpBinding"
              behaviorConfiguration="CookieBehaviorConfig"
              contract="Telerik.Sitefinity.Security.Web.Services.IUsers" />
    <endpoint name="PagesServiceEndpoint"
              binding="webHttpBinding"
              bindingConfiguration="PagesServiceBinding"
              behaviorConfiguration="CookieBehaviorConfig"
              contract="Telerik.Sitefinity.Modules.Pages.Web.Services.IPagesService" />
  </client>
</system.serviceModel>

Then you can very simply call the User service to authenticate your user, the cookie is persisted between WCF calls, and used when calling the PagesService:

var usersService = new WebChannelFactory<IUsers>("UserServiceEndpoint");
var usersChannel = usersService.CreateChannel();
var auth = usersChannel.AuthenticateUser(new Telerik.Sitefinity.Security.Credentials() UserName = "username", Password = "password", Persistent=true );
 
var pagesService = new WebChannelFactory<IPagesService>("PagesServiceEndpoint");
var pagesChannel = pagesService.CreateChannel();
var page = pagesChannel.GetPage("096fd910-40d7-4887-a9e6-fff465920774", null, false);

Note that if the user is already authenticated to the site when attempting to this call, the authentication will fail, so you'll need to log the user out. That part of the example is an exercise left to the developer.

Hopefully this helps someone else, as I've spent the last few days attempting to work with the web services to enable custom fields on pages, so far to no avail.

Posted by Community Admin on 12-Aug-2011 00:00

Hello Greg,

Actually this is the only way for now to authenticate, because we don't have a specific service that you can use to authenticate a user or an existing service where you can pass the parameters.

You might find this post ( call service from javascript)useful.

Best wishes,
Ivan Dimitrov
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>

This thread is closed