Consuming SF 4 REST service
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
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
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();
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
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!
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
);
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();
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:
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
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
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
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.
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
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?
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
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?
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
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.
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
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
;
<
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"
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
>
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
);
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