Skip to main content
Announcements
Have questions about Qlik Connect? Join us live on April 10th, at 11 AM ET: SIGN UP NOW
cancel
Showing results for 
Search instead for 
Did you mean: 
Anonymous
Not applicable

Exporting an App using REST API in C# .NET

Hi everyone, we're setting up a small environment where I will be automating the transfer of Qlik Sense Apps to different engines on different servers. Using the APIs I've been able to Import, Publish and get a ticket for Exporting an app, but not been able to use the APIs to Export.

I'm probably missing something obvious, but it's not clear to me with the Export call:

https://help.qlik.com/en-US/sense-developer/3.1/Subsystems/RepositoryServiceAPI/Content/RepositorySe...

How do I deal with the body of the request returned? I've written the content directly to the file system, but it's not a proper app when opened, so I'm not sure if I have to remove headers, decompress, or even look on the file system on the server or somewhere else (as you have to specify your path) to get the file export working. It looks like the text returned is almost a Qlik Sense App (comparing it with the original).

I've also got task triggering working in C# via this example (and got the other API calls working): Working with REST API to trigger reload tasks

My code is:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(strServerName + @":4242/qrs/download/app/" + strAppID + "/" + strRequestGUID + "/" + strFilename + "?xrfkey=" + xrfkey);

request.Method = "GET";
request.Accept = "application/vnd.qlik.sense.app";
request.Headers.Add("X-Qlik-xrfkey", xrfkey);
request.Headers.Add("id", strAppID);

// Add the certificate to the request and provide the user to execute as
request.ClientCertificates.Add(SenseCert);
request.Headers.Add("X-Qlik-User", strUserStringForQlik);

//Send the request
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{


  //So at this point, we have a stream when put into raw text looks like 
  //  a Qlik Sense App, but is not a Qlik Sense app. 

  using (StreamWriter sw = new StreamWriter(strFilename, false, Encoding.UTF8))
  {
   StreamReader sr = new StreamReader(stream, Encoding.UTF8);

   //We write the data to a file here, but it won't load in Qlik Sense

   while (!sr.EndOfStream)
   {

    string strLinedata = sr.ReadLine();

    sw.WriteLine(strLinedata);
   }
  }

}
}

1 Solution

Accepted Solutions
Anonymous
Not applicable
Author

So after some more research, I have figured it out. TL;DR the data returned in the request is a byte stream, and should be written as a byte stream not as a string.

I had thought of using a byte stream, but since I have never used one of those before I used the wrong code, and it didn't work, so I kept on trying with the streamwriter. The streamwriter writes strings, and hence when I looked at the file, the text parts looked like a proper .QVF, but nothing else did.

Honestly I'm surprised there's no easier way of writing a byte stream in C#, but at least it works!

Code assume previous setup has been done, insert this over the top of the response:


//Send the request
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
  // Create the local file
  Stream localStream = File.Create(strFilename);
  int bytesProcessed = 0;

  // Allocate a 1k buffer
  byte[] buffer = new byte[1024];
  int bytesRead;

  // Simple do/while loop to read from stream until
  // no bytes are returned
  do
  {
   // Read data (up to 1k) from the stream
   bytesRead = stream.Read(buffer, 0, buffer.Length);

   // Write the data to the local file
   localStream.Write(buffer, 0, bytesRead);

   // Increment total bytes processed
   bytesProcessed += bytesRead;
  } while (bytesRead > 0);
}
}

I guess it should have been obvious, but clearly the obvious takes weeks to figure out whilst other things are being done!

View solution in original post

5 Replies
s29124141
Partner - Creator II
Partner - Creator II

david.mauriceYou must subclass and override one or more functions.

class MyWebClient : WebClient

    {

        protected override WebRequest GetWebRequest(Uri address)

        {

            HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);

            request.ClientCertificates.Add(SenseCert);

            return request;

        }

    }

Use MyWebClient as below to export app.

using (var client = new MyWebClient())

            {

                client.DownloadFile(strServerName + @":4242/qrs/download/app/" + strAppID + "/" + strRequestGUID + "/" + strFilename + "?xrfkey=" + xrfkey, "app.qvf");

               

            }

Anonymous
Not applicable
Author

That is my fault, I did not include the Certificate code in my example, (there is nothing proprietary in it so I should have).

  // locate the client certificate and accept it as trusted

   X509Certificate2 SenseCert = new X509Certificate2(strCertificatePath, "", X509KeyStorageFlags.MachineKeySet);

   ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

and this certificate "SenseCert" is then passed in. It is the code I use for the other requests I make.

Anonymous
Not applicable
Author

So after some more research, I have figured it out. TL;DR the data returned in the request is a byte stream, and should be written as a byte stream not as a string.

I had thought of using a byte stream, but since I have never used one of those before I used the wrong code, and it didn't work, so I kept on trying with the streamwriter. The streamwriter writes strings, and hence when I looked at the file, the text parts looked like a proper .QVF, but nothing else did.

Honestly I'm surprised there's no easier way of writing a byte stream in C#, but at least it works!

Code assume previous setup has been done, insert this over the top of the response:


//Send the request
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
  // Create the local file
  Stream localStream = File.Create(strFilename);
  int bytesProcessed = 0;

  // Allocate a 1k buffer
  byte[] buffer = new byte[1024];
  int bytesRead;

  // Simple do/while loop to read from stream until
  // no bytes are returned
  do
  {
   // Read data (up to 1k) from the stream
   bytesRead = stream.Read(buffer, 0, buffer.Length);

   // Write the data to the local file
   localStream.Write(buffer, 0, bytesRead);

   // Increment total bytes processed
   bytesProcessed += bytesRead;
  } while (bytesRead > 0);
}
}

I guess it should have been obvious, but clearly the obvious takes weeks to figure out whilst other things are being done!

s29124141
Partner - Creator II
Partner - Creator II

Easier way of writing a byte stream or downloading a file in C#, is as mentioned in my above answer using WebClient.


class MyWebClient : WebClient

    {

        protected override WebRequest GetWebRequest(Uri address)

        {

            HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);

            request.ClientCertificates.Add(SenseCert);

            return request;

        }

    }

Use MyWebClient as below to export app.

using (var client = new MyWebClient())

            {

                client.DownloadFile(strServerName + @":4242/qrs/download/app/" + strAppID + "/" + strRequestGUID + "/" + strFilename + "?xrfkey=" + xrfkey, "app.qvf");

          

            }

Anonymous
Not applicable
Author

I've had a look, and yes your answer is a lot neater, but we're actually using the same classes.

WebClient does use its internal WebRequest class (which you override to add the cert) to access resources

https://msdn.microsoft.com/en-us/library/system.net.webclient(v=vs.110).aspx

I use HTTPWebrequest, which inherits WebRequest

https://msdn.microsoft.com/en-us/library/system.net.webrequest(v=vs.110).aspx

With HTTPWebrequest, you get to add certs and other HTTP properties directly, but in your code you get to use the DownloadFile method of WebClient directly, rather than having to loop through items to get and write the file as I do

I'd argue that you could setup a HTTPWebRequest class with parameters passed in and then override WebClients internal WebRequest to get all the functionality in a single method, but for me that's when I have some free time *laughs*