cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
garth
Community Champion

File Upload API

Jump to solution


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?

1 Solution
garth
Community Champion

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.

View solution in original post

6 Replies
garth
Community Champion

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?

holmer
Community Member

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.

garth
Community Champion

 @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.

garth
Community Champion

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",

  "url": "https://xxx.test.instructure.com/files/47073/download?download_frd=1\\u0026verifier=kBJ375302R4NbJay...",

  "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 : )

garth
Community Champion

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.

garth
Community Champion

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.