The Instructure Community will enter a read-only state on November 22, 2025 as we prepare to migrate to our new Community platform in early December.
Read our blog post for more info about this change.
Found this content helpful? Log in or sign up to leave a like!
I have been working through the API portion of this course: Canvas Dev and Friends
(I completed the LTI portion : )
Lesson 6 walks you through file uploads, and I am stuck.
My initial request to upload a file is returning the upload url and upload_params, no problem.
However when I try to post the file I get back StatusCode: 403, ReasonPhrase: 'Forbidden'
I am following the documentation found here: Uploading Files - Canvas LMS REST API Documentation
and am not sure why this isn't working.
I am working with .NET, using the HttpClient, and have verified that the upload_params are being added in the order they were received, and that "file" is the last parameter to be added to the HttpContent. I have added the file as "StreamContent", and "ByteArrayContent".
Has anyone else experienced this?
Solved! Go to Solution.
Well, once you know the root cause the solution comes easily.
Add a WebRequestHandler to the constructor of HttpClient, and don't allow redirects:
WebRequestHandler handler = new WebRequestHandler();
handler.AllowAutoRedirect = false;
using (HttpClient client = new HttpClient(handler))
....
Now I know, I hope someone else finds this useful.
I found the cause of the 403 Forbidden response, and have corrected the issue.
However, now I am getting back a status 200 OK
I am expecting a 301 with a Location, per the API document for phase 2:
HTTP/1.1 301 Moved Permanently
Location: https://<canvas>/api/v1/s3_success/1234?uuid=ABCDE
Does anyone know why I would be getting status 200 instead of 301?
What was the cause of your 403 error and how did you fix it? I am having a similar problem but I am developing with Java. Would love to get your help with my issues.
@holmer I was getting a status 303, which is a redirect.
.NET was doing some stuff on the backend that I needed to catch, then it was easy.
Basically I told .NET not to automatically redirect, let me decide.
I wrote a test app that will walk you through the steps, even though you are using Java you can take a look at the steps I'm taking. The project is here:
I tried not to "optimize" the code so it would be easy to follow.
Let me know if that helps, hopefully there are enough comments, I haven't looked at the project in a while.
As I mentioned, I'm working with .NET
Here is the code to upload the file to the "upload_url" that I receive from the first step in the process.
NOTE: [dres] represents the results returned by Canvas in the initial request:
//PHASE 2: upload file to upload_url and get back the location needed for PHASE 3
using (HttpClient client = new HttpClient())
{
MultipartFormDataContent content = new MultipartFormDataContent();
//add upload_params to form data in same order we received it
foreach (dynamic key in dres.upload_params)
{
content.Add(new StringContent(key.Value.ToString()), key.Name.ToString());
}
//read file to upload into byte array
FileStream fs = fi.OpenRead();
byte[] fileBytes = new byte[fi.Length];
int numBytes = fs.Read(fileBytes, 0, fileBytes.Length);
fs.Close();
//add byte array as last element of the form data, per Canvas document
content.Add(new ByteArrayContent(fileBytes), "file");
//post for data to the upload_url defined by Canvas
HttpResponseMessage response = await client.PostAsync(dres.upload_url.ToString(), content);
string s = await response.Content.ReadAsStringAsync();
}
The response I receive, actually from AWS is as follows.
Reponse Headers:
{StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization, Accept-Encoding
Access-Control-Allow-Methods: POST, PUT, DELETE, GET, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Request-Method: *
Status: 200 OK
Vary: Accept-Encoding
X-Canvas-Meta: q=945;a=1;g=2xCP3OOBwsD1e64Bp1ewJCIZXn3VfkvonsHLrRNG;s=6124;c=cluster24;z=us-east-1b;o=files;n=api_create_success;b=1059700;m=1059700;u=0.12;y=0.01;d=0.13;
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Request-Context-Id: 1ee795eb-a183-47a9-88d9-c3586816891b
X-Request-Processor: a023c27
X-Robots-Tag: none
X-Runtime: 0.387712
X-UA-Compatible: IE=Edge,chrome=1
X-XSS-Protection: 1; mode=block
Connection: keep-alive
Cache-Control: must-revalidate, max-age=0, private
Date: Tue, 14 Jun 2016 17:01:50 GMT
ETag: "1c749fd27a7b93ccd35441126e00a559"
P3P: CP="None, see http://www.instructure.com/privacy-policy"
Set-Cookie: _csrf_token=Q8s%2BNe8I63l7jOewJsr3IjFBYd6U3dqKnoczaflynkQrilh6nWm7SBT8sP9nsJFkBzsghN6F9b2rz0cDoRnmKQ%3D%3D; path=/; secure
Server: Apache
Content-Length: 553
Content-Type: application/json; charset=utf-8
}}
And the response body as follows:
{
"id": 47073,
"folder_id": 322,
"display_name": "test2.txt",
"filename": "test2.txt",
"content-type": "text/plain",
"size": 11,
"created_at": "2016-06-14T16:59:59Z",
"updated_at": "2016-06-14T17:01:50Z",
"unlock_at": null,
"locked": false,
"hidden": false,
"lock_at": null,
"hidden_for_user": false,
"thumbnail_url": null,
"modified_at": "2016-06-14T16:59:59Z",
"locked_for_user": false,
"preview_url": "/users/xx/files/xxxxx/file_preview?annotate=0"
}
The problem is that the response status is 200, not 301
Also, the response body does not contain the "Location" value.
To quote the Canvas document: Uploading Files - Canvas LMS REST API Documentation
"The parameters POSTed with this request come directly from the upload_params part of the JSON response in Step 1. The only addition is the file parameter which must be posted as the last parameter following all the others."
I have followed these instructions, and using the code above, the file does upload and appears and is accessible in the target Canvas folder.
But why am I not getting a status 301 response with the "Location"??
If anyone has any ideas I would appreciate any feedback : )
For anyone following this, I configured <system.diagnostics> to dump System.Net output to the output window in Visual Studio.
Doing this showed me that I was getting multiple responses from the PostAsync call.
On the first attempt to Post the form data, HttpClient recieves back the 30X response, which logs like this:
System.Net Warning: 0 : [6760] HttpWebRequest#30617370::() - Error code 303 was received from server response.
System.Net Warning: 0 : [6760] HttpWebRequest#30617370::() - Resubmitting request.
HttpClient then re-posts the request, and receives a status 200 for the second resposne, which is what is returned to the caller.
This is not something that I have dealt with before, I am working on resolution.
Well, once you know the root cause the solution comes easily.
Add a WebRequestHandler to the constructor of HttpClient, and don't allow redirects:
WebRequestHandler handler = new WebRequestHandler();
handler.AllowAutoRedirect = false;
using (HttpClient client = new HttpClient(handler))
....
Now I know, I hope someone else finds this useful.
Community helpTo interact with Panda Bot, our automated chatbot, you need to sign up or log in:
Sign inTo interact with Panda Bot, our automated chatbot, you need to sign up or log in:
Sign in