Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.

Reply
Anonymous
Not applicable

API Import 400 Bad Request Error when uploading pbix file

Hello,

 

I have an application where we want to show PowerBI Premium reports in our application.  However, before that happens, we also need to upload PBIX reports using our application.  I have been able to get the proper token, and when I run any get requests (like GetReports), on the API, I get the data back just fine. 

 

However, when I run an Import on the api, it always says 400 Error Bad Request.  I am going to post my code below and would love any help I could get.  I have tried reading in the file from disk, instead of passing it in using FileSelctor tool, I have tried uising different types of requests, made sure I have all permissions assigned to my app through Azure Portal, ect... and nothing has worked. Pretty much I have exausted my thoughts on how to proceed,  It is just strange that every read seems to work great, but a post will not.  

 

All the setup seems to be in place. The only setup I have not done is create Workspace Collections in Azure. The reason for this is it was my understanding that this was part of the old embeded style.  I do have a new AppWorkspace that was created using my Premium account in PowerBI itself.  Not sure if this has something to do with why an import is not working or what, but wanted to give as much info as possible so I can get help ASAP.

 

Thank you all very much, and I look forward to hearing back from you.

 

Steven 

 

 

public async Task Upload([FromUri] int ReportId, [FromUri] string reportName, [FromUri] bool overwrite)
        {
            try
            {

                if (!Request.Content.IsMimeMultipartContent("form-data"))
                {
                    throw new HttpResponseException(HttpStatusCode.BadRequest);
                }

                var provider = await Request.Content.ReadAsMultipartAsync<InMemoryMultipartFormDataStreamProvider>(new InMemoryMultipartFormDataStreamProvider());

                //access form data
                NameValueCollection formData = provider.FormData;

                //access files
                IList<HttpContent> files = provider.Files;
                //Example: reading a file's stream like below
                HttpContent file1 = files[0];
               // Stream file1Stream = await file1.ReadAsStreamAsync();

                byte[] fileToSend = await file1.ReadAsByteArrayAsync();

                // Create a user password cradentials.
                var credential = new UserCredential(Username, Password);
                
// Authenticate using created credentials
                var authenticationContext = new AuthenticationContext(AuthorityUrl);
                var authenticationResult = await authenticationContext.AcquireTokenAsync(ResourceUrl, ClientId2, credential);
                token = authenticationResult.AccessToken;
                var tokenCredentials = new TokenCredentials(authenticationResult.AccessToken, "Bearer");
                
                var nameConflict = "Overwrite";
                string responseContent = string.Empty;

                //Configure dashboards request
                System.Net.WebRequest request = System.Net.WebRequest.Create("https://api.powerbi.com/v1.0/myorg/imports?datasetDisplayName=TestImport&nameConflict=Overwrite") as System.Net.HttpWebRequest;
                request.Method = "POST";
                
// Set the content type of the data being posted.
                request.ContentType = "multipart/form-data";

                // Set the content length of the string being posted.
                request.ContentLength = fileToSend.Length;
                request.Headers.Add("Authorization", String.Format("Bearer {0}", token));

                using (Stream requestStream = request.GetRequestStream())
                {
                    requestStream.Write(fileToSend, 0, fileToSend.Length);
                    requestStream.Close();
                }

                //Get dashboards response from request.GetResponse()
                using (var response = request.GetResponse() as System.Net.HttpWebResponse)
                {
                    //Get reader from response stream
                    using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
                    {
                        responseContent = reader.ReadToEnd();
                    }
                }
            }
            catch (Exception ex)
            {
                var junk = true;
            }
        }
    }
1 ACCEPTED SOLUTION

For new reports just remove the name conflict parameter 

View solution in original post

6 REPLIES 6
Eric_Zhang
Employee
Employee

@Anonymous

I see you are appending the nameConflict parameter to the URL, one most probably reason I can think of for your 400 error is that the url with nameConflict parameter would throw error if the report you'd like to import doesn't already exist. 

https://api.powerbi.com/v1.0/myorg/imports?datasetDisplayName=TestImport&nameConflict=Overwrite

By the way, I don't find /groups/{groupid} in the URL. Do note the that you can only embed the reports from a created app workspace.

 

For better troubleshooting, I'd suggest you add an extra catch block.

catch (WebException wex)
            {
                if (wex.Response != null)
                {
                    using (var errorResponse = (HttpWebResponse)wex.Response)
                    {
                        using (var reader = new StreamReader(errorResponse.GetResponseStream()))
                        {
                            string errorString = reader.ReadToEnd();
                            dynamic respJson = JsonConvert.DeserializeObject<dynamic>(errorString);
                            Console.WriteLine(respJson.ToString());
                            //TODO: use JSON.net to parse this string and look at the error message
                        }
                    }
                }
            }

 

Also you can reference my import pbix file demo.

using System;
using System.Net;
//Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory -Version 2.21.301221612
using Microsoft.IdentityModel.Clients.ActiveDirectory;

//Install-Package Newtonsoft.Json 
using Newtonsoft.Json;
using System.IO;
using System.Threading.Tasks;

namespace ConsoleApplication39
{

    class Program
    {

        //Step 1 - Replace {client id} with your client app ID. 
        //To learn how to get a client app ID, see Register a client app (https://msdn.microsoft.com/en-US/library/dn877542.aspx#clientID)
        private static string clientID = "{client id}";
          
        //Resource Uri for Power BI API
        private static string resourceUri = "https://analysis.windows.net/powerbi/api";

        //OAuth2 authority Uri
        private static string authorityUri = "https://login.windows.net/common/oauth2/authorize";
         
        private static string token = String.Empty;

        //Uri for Power BI datasets
        private static string pbiEndpoint = "https://api.powerbi.com/v1.0/myorg"; 

        //Example dataset name and group name 
        private static string groupId = "{group id}";

        static void Main(string[] args)
        {
            //Import sample 
            string pbixPath = @"C:\test\KPI.pbix";
            string datasetDisplayName = "mydataset";

            Task t = Import(string.Format("{0}/groups/{1}/imports?datasetDisplayName={2}&nameConflict=Overwrite", pbiEndpoint, groupId, datasetDisplayName), pbixPath);
            t.Wait();

            Console.ReadKey();

        }

        public static async Task<string> Import(string url, string fileName)
        {
            string responseStatusCode = string.Empty;

            string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
            byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

            request.ContentType = "multipart/form-data; boundary=" + boundary;
            request.Method = "POST";
            request.KeepAlive = true;

            var credential = new UserCredential(yourPbiAccount, Password);

            // Authenticate using created credentials
            var authenticationContext = new AuthenticationContext(authorityUri);
            var authenticationResult = await authenticationContext.AcquireTokenAsync(resourceUri, clientID, credential);
            token = authenticationResult.AccessToken;


            request.Headers.Add("Authorization", String.Format("Bearer {0}", token.ToString()));

            using (Stream rs = request.GetRequestStream())
            {
                rs.Write(boundarybytes, 0, boundarybytes.Length);

                string headerTemplate = "Content-Disposition: form-data; filename=\"{0}\"\r\nContent-Type: application / octet - stream\r\n\r\n";
                string header = string.Format(headerTemplate, fileName);
                byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
                rs.Write(headerbytes, 0, headerbytes.Length);

                using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
                {
                    byte[] buffer = new byte[4096];
                    int bytesRead = 0;
                    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        rs.Write(buffer, 0, bytesRead);
                    }
                }

                byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
                rs.Write(trailer, 0, trailer.Length);
            }
            try
           {
                using (HttpWebResponse response = request.GetResponse() as System.Net.HttpWebResponse)
                {
                    responseStatusCode = response.StatusCode.ToString();

                    Console.WriteLine("Import pbix file is {0}", responseStatusCode);
                }
            }
            catch (WebException wex)
            {
                if (wex.Response != null)
                {
                    using (var errorResponse = (HttpWebResponse)wex.Response)
                    {
                        using (var reader = new StreamReader(errorResponse.GetResponseStream()))
                        {
                            string errorString = reader.ReadToEnd();
                            dynamic respJson = JsonConvert.DeserializeObject<dynamic>(errorString);
                            Console.WriteLine(respJson.ToString());
                            //TODO: use JSON.net to parse this string and look at the error message
                        }
                    }
                }
            } 

            return responseStatusCode;
        }
         
    }
}

 

Anonymous
Not applicable

@Eric_Zhang

 

When I try to upload a file that previously exists using the URL with ?datasetDisplayName={2}&nameConflict=Overwrite at the end, it works great.  However when the report is a new report, and I try to upload it using...

 

https://api.powerbi.com/v1.0/myorg/groups/{GroupID}/imports

 

It does not update.  In the code that you gave me to catch the error, all I see is the word Message: "".  It does not tell me anything more. Previously you said that if you try to import a report, that does not exist, and use the ?datasetDisplayName={2}&nameConflict=Overwrite at end of URL, that it will not work.  Well it seems even without that it does not work. Any thoughts why?


Thanks,


Stizz001

For new reports just remove the name conflict parameter 

Anonymous
Not applicable

Yea, i ended up figuring that out late today. Sorry for not updating the thread. I appreciate all your help. I wpuld have struggled a lot more than I did without it.

Hi @Anonymous , 

 

I am getting <Response [400]>, when I run this code. Any idea what is wrong ?

 

Thanks in advance.

 

import requests

values = """
-----BOUNDARY
Content-Disposition: form-data; name="mypbix"; filename="mypbix.pbix"
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64

{PBIX binary data}
-----BOUNDARY"""

headers = {
'Content-Type': 'multipart/form-data; boundary=---BOUNDARY',
'Authorization': 'Bearer <access_token>'
}
request = requests.post('https://api.powerbi.com/v1.0/myorg/groups/<group_id>/imports?datasetDisplayName=ProductOrder', data=values, headers=headers)

 

Anonymous
Not applicable

@Eric_Zhang

 

I apprecaite your response and am sorry I did not get back to you earlier.  I decided for the time being to work on the front end stuff and get the PowerBI reports to show up in the app.  I have pretty much finished that now, and am going back to finalizing the import.  

 

I tried your below code, and and was still getting an error as I had deleted the uploaded report and thus removed the part about overwritting the data from the end of the url.  This broke.  But when I put that back on, it all seems to have worked great.  

 

With that said though, I have 2 last questions.  What if I already had the file in memory, and did not want to save it locally?  In my app I use a tool that finds the tile and in the web request I get the file. I can convert this to byets or a stream, but I am confused as to what would go in these lines then....

string headerTemplate = "Content-Disposition: form-data; filename=\"{0}\"\r\nContent-Type: application / octet - stream\r\n\r\n";
                string header = string.Format(headerTemplate, fileName);

 

Secondly,  is the only way to know if said report already exists to check call the API to get the reports and check to see if one with that name already exists?  I ask because it seems to me that I need to modify the URL depnding on if the report exists previously or not.   So is that the best way to handel this, since you said having the overwirte and dataSetDisplayName on the URL if the report does not exist throws an error?

 

Either.....

string.Format("{0}/groups/{1}/imports?datasetDisplayName={2}&nameConflict=Overwrite", pbiEndpoint, groupId, datasetDisplayName), pbixPath

OR

 

string.Format("{0}/groups/{1}/imports?", pbiEndpoint, groupId), pbixPath

 

Thanks a ton.

 

Stizz001

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

Find out what's new and trending in the Fabric Community.