Forum Discussion
API Import 400 Bad Request Error when uploading pbix file
- 9 years ago
For new reports just remove the name conflict parameter
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;
}
}
}
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), pbixPathOR
string.Format("{0}/groups/{1}/imports?", pbiEndpoint, groupId), pbixPath
Thanks a ton.
Stizz001