Download a file from a POST WebAPI call

Hi!  

We are currently passing in a folderId over a WebAPI call to Appian - from there we are using a!startProcess to run the Document Zipper smart service to zip the folder and output the resulting documentId.

Is there a way to allow the resulting zip document to be downloaded via the WebAPI call, or will I have to use some workaround like using an additional GET method to pull the document after using POST to run the smart service?

Thanks!

  Discussion posts and replies are publicly visible

  • As long as you are chaining all the way through your process model this could be potentially possible. Then, in the onsuccess parameter of a!startProcess you should return:

    a!httpResponse(
          headers: if(
            http!request.queryParameters.attachment,
            a!httpHeader(
              name: "Content-Disposition",
              value: concat(
                "attachment; filename=""",
                document(fv!processInfo.pv.documentId, "name"),
                ".",
                document(fv!processInfo.pv.documentId, "extension"),
                """"
              )
            ),
            {}
          ),
          body: todocument(fv!processInfo.pv.documentId)
        )

    I have never done this and our API template creates the general download document requests as a GET but I think it is worth attempting. Otherwise, you will most likely need to make two calls.

  • Hi Danny, thanks for your reply!  My code almost perfectly mirrors yours (I modified the download document template).  I'll attach my code snippet, in case I am missing something; however I'm still getting the following error: "Not a valid Web API Response. Received: [Lcom.appiancorp.core.data.Record;@2127ea54".

    a!localVariables(
      /* We don't want to serve arbitrary documents because it could be a security risk */
      local!extensionWhitelist: {"zip"},
      a!startProcess(
        processModel: cons!JSB_PM_SEND_DOCUMENT,
        processParameters: {folderId: http!request.formData.folderId},
        onSuccess: {
          if(
            /*
            * The path must be only 1 value, the document ID, and that ID must be a number.
            * Otherwise, we return a 404 Not Found
            */
            or(
              isnull(fv!processInfo.pv.zippedDocument),
              not(contains(local!extensionWhitelist, document(fv!processInfo.pv.zippedDocument, "extension")))
            ),
            a!httpResponse(
              statusCode: 404,
              body: "404 Not Found",
              headers: {}
            ),
            a!httpResponse(
              /*
              * If the query parameter "attachment" is set to true,
              * set an HTTP header that tells the client that the body of the response
              * should be downloaded. This overrides the default content-disposition
              * of "inline; filename=<filename>.<extension>". We will automatically
              * set the content-type and length.
              */
              headers: if(
                http!request.formData.attachment,
                a!httpHeader(
                  name: "Content-Disposition",
                  value: concat(
                    "attachment; filename=""",
                    document(fv!processInfo.pv.zippedDocumentId, "name"),
                    ".",
                    document(fv!processInfo.pv.zippedDocumentId, "extension"),
                    """"
                  )
                ),
                {}
              ),
              body: todocument(fv!processInfo.pv.zippedDocumentId)
            )
          )
        },
        onError: a!httpResponse(
          statusCode: 500,
          body: "500 There was an error starting the process",
          headers: {}
        )
      )
    )

  • My guess is we only allow downloading a document via a GET. You can verify this by trying to download a document via GET and if it works just change your call to a POST. Given the low-latency to call Appian, calling back to back requests shouldn't add too much overhead.

  • I've tested that already - this actually started as a GET method until I realized you can only call a!startProcess from a POST method.  It looks like I'll just have to use two calls, or just zip the file whenever a document within it gets modified.

  • 0
    Certified Lead Developer
    in reply to Danny Verb

    Can I call the web api link in a!safeLink

  • I recommend against it. If you need to download a document, you can use a!documentDownloadLink()

  • 0
    Certified Lead Developer
    in reply to Jacob Benjamin

    I literally just ran into the same problem. My onSuccess was returning a list with an a!httpResponse within it (like you're doing here) when I removed the curly brackets it worked.

    basically:

    onSuccess: {
          if(
            /*
            * The path must be only 1 value, the document ID, and that ID must be a number.
            * Otherwise, we return a 404 Not Found
            */
            or(
              isnull(fv!processInfo.pv.zippedDocument),
              not(contains(local!extensionWhitelist, document(fv!processInfo.pv.zippedDocument, "extension")))
            ),
            a!httpResponse(
              statusCode: 404,
              body: "404 Not Found",
              headers: {}
            ),
            a!httpResponse(
              /*
              * If the query parameter "attachment" is set to true,
              * set an HTTP header that tells the client that the body of the response
              * should be downloaded. This overrides the default content-disposition
              * of "inline; filename=<filename>.<extension>". We will automatically
              * set the content-type and length.
              */
              headers: if(
                http!request.formData.attachment,
                a!httpHeader(
                  name: "Content-Disposition",
                  value: concat(
                    "attachment; filename=""",
                    document(fv!processInfo.pv.zippedDocumentId, "name"),
                    ".",
                    document(fv!processInfo.pv.zippedDocumentId, "extension"),
                    """"
                  )
                ),
                {}
              ),
              body: todocument(fv!processInfo.pv.zippedDocumentId)
            )
          )
        },

    should become:

    onSuccess: if(
            /*
            * The path must be only 1 value, the document ID, and that ID must be a number.
            * Otherwise, we return a 404 Not Found
            */
            or(
              isnull(fv!processInfo.pv.zippedDocument),
              not(contains(local!extensionWhitelist, document(fv!processInfo.pv.zippedDocument, "extension")))
            ),
            a!httpResponse(
              statusCode: 404,
              body: "404 Not Found",
              headers: {}
            ),
            a!httpResponse(
              /*
              * If the query parameter "attachment" is set to true,
              * set an HTTP header that tells the client that the body of the response
              * should be downloaded. This overrides the default content-disposition
              * of "inline; filename=<filename>.<extension>". We will automatically
              * set the content-type and length.
              */
              headers: if(
                http!request.formData.attachment,
                a!httpHeader(
                  name: "Content-Disposition",
                  value: concat(
                    "attachment; filename=""",
                    document(fv!processInfo.pv.zippedDocumentId, "name"),
                    ".",
                    document(fv!processInfo.pv.zippedDocumentId, "extension"),
                    """"
                  )
                ),
                {}
              ),
              body: todocument(fv!processInfo.pv.zippedDocumentId)
            )
          ),

    Appian expects a return value of type httpResponse, and anything other than that will cause an error (including list of variant and list of httpResponse)

    No idea if you still need an answer on this, but this was what came up when I googled the error.