onc-healthit / reference-ccda-validator

Deployable C-CDA Validator war source code. Use this repository to build and deploy a validator on your local environment.
BSD 2-Clause "Simplified" License
36 stars 37 forks source link

Attempting to validate a CDA using the API and I'm missing something #84

Closed LHAMPTON closed 3 years ago

LHAMPTON commented 4 years ago

With the service running locally, I can open the \static\validationui.html page and validate a document. Now I'm trying to do the same thing from code using the API as documented on the swagger UI page. In essence, I'm POSTing the file content to the address:

http://localhost:8080/referenceccdaservice/dovalidation

and the response/error is "not found".

To what local address should I post the file in order to have it validate?

I see a validator_service.js and other files in static/js, but if that's what I need to call, I'm not sure how to format it within the post. (I've tried several variations but have been unable to make it work.)

I'm placing the call from DotNet using HttpWebRequest.

Thanks

LHAMPTON commented 4 years ago

After further reading and head-scratching, I think I understand a bit better; everything needs the Swagger-ui file. Unfortunately, the only html file that was populated in the service's Static folder is: validationui.html

If I understand correctly, I need the swagger-ui.html file and it is nowhere to be found. I've searched through the on-line resources and still don't find it. Where do I get a copy so that, once again, I can attempt to get this going?

Thanks

LHAMPTON commented 4 years ago

I found issue # 44 wherein someone else was having a similar issue. I tried opening the swagger-ui in a browser and it sorta waves its hand at me but outputs the message:

fetching resource list: http://localhost:8080/referenceccdaservice/v2/api-docs; Please wait.

and never goes further.

drbgfc commented 4 years ago

This is an incomplete answer as I have created a ticket for this issue and we will prioritize. But, so your not left with nothing: If you go to http:/localhost:8080/referenceccdaservice/static/validationui.html you will see what the purpose is of the static ui. It is for your use with a UI. It is not for use as an endpoint. If you want to post the validation to an endpoint, use, https://localhost:8080/referenceccdaservice/ which is a POST call. The ticket will look into remaining issues such as potential swagger issues, addressing them, and updating documentation to explain it better. Hope that helps address your main concern for now

LHAMPTON commented 4 years ago

Through trial and error, I've finally been able to send something to the validator without it rejecting my POST. But then the connection times out before I receive a response. Currently using the default timeout of 100 seconds; seems like it should be enough.

Just to be sure,

I am opening the connection with the following string: http://localhost:8080/referenceccdaservice?validationObjective=CCDA_IG_Only&referenceFileName=&severityLevel=ERROR method is POST, and content type is application/x-www-form-urlencoded then writing the file content (one of the samples that we need to validate for certification) to the request stream.

I was hoping that if there were required parameters that I'm not sending, it would respond with a complaint.

I also check the logs and don't see any entries for today other than an empty localhost_access_log_2020-11-13.txt. No referenceccdaservice log with today's date.

Ideas?

Thanks

drbgfc commented 3 years ago

SITE-3175

LHAMPTON commented 3 years ago

My development computer was rebooted over the weekend, thanks to a Windows update. Now, after re-starting the service and attempting to POST a validation request (as noted above), I get an immediate error response of [405] Method not allowed. When I look at the validator log, I see the following lines ( I've included the end of the startup just to show the full context):

08:31:48,155 INFO [VsacLoader:35] Loading Value Set File: SurgicalOperationNoteDocumentTypeCode.xlsx 08:31:48,164 INFO [VsacLoader:35] Loading Value Set File: TargetSiteQualifiers.xlsx 08:31:48,171 INFO [VsacLoader:35] Loading Value Set File: UnitsOfMeasureCaseSensitive.xlsx 08:31:48,200 INFO [VocabularyLoadRunner:101] Value Sets loaded... 08:31:48,200 INFO [VocabularyLoadRunner:105] !!!! VOCABULARY DATABASE HAS FINISHED LOADING - SERVER WILL CONTINUE AND SHOULD BE DONE SHORTLY. !!!! 08:31:48,248 INFO [DefaultLifecycleProcessor:341] Starting beans in phase 2147483647 08:31:48,249 INFO [DocumentationPluginsBootstrapper:120] Context refreshed 08:31:48,262 INFO [DocumentationPluginsBootstrapper:123] Found 1 custom documentation plugin(s) 08:31:48,270 INFO [ApiListingReferenceScanner:44] Scanning for api listing references 08:31:48,416 INFO [ContextLoader:347] Root WebApplicationContext: initialization completed in 81065 ms 08:31:48,421 INFO [DispatcherServlet:489] FrameworkServlet 'dispatcher': initialization started 08:31:48,422 INFO [AnnotationConfigWebApplicationContext:578] Refreshing WebApplicationContext for namespace 'dispatcher-servlet': startup date [Wed Nov 18 08:31:48 EST 2020]; parent: Root WebApplicationContext 08:31:48,441 INFO [DispatcherServlet:508] FrameworkServlet 'dispatcher': initialization completed in 20 ms 08:43:28,329 WARN [PageNotFound:208] Request method 'GET' not supported 08:45:20,087 WARN [PageNotFound:208] Request method 'GET' not supported

I've no idea what "GET" is not supported since I am issuing a POST request.

I'm getting a bit desperate here since I need this for certification and the EOY is fast approaching. If you offer any paid support services, I would be open to purchasing a couple of hours so that we can get this working. Thanks

drbgfc commented 3 years ago

You want to use form-data for encoding As an example from Postman: image

All other params would go here as well, here is the list, from the source:

    @RequestMapping(value = "/", headers = "content-type=multipart/*", method = RequestMethod.POST)
    public ValidationResultsDto doValidation(
            @RequestParam(value = "validationObjective", required = true) String validationObjective,
            @RequestParam(value = "referenceFileName", required = true) String referenceFileName,
            @RequestParam(value = "ccdaFile", required = true) MultipartFile ccdaFile,
            @RequestParam(value = "curesUpdate", required = false) boolean curesUpdate,  
            @RequestParam(defaultValue = VocabularyConstants.Config.DEFAULT, required = false) String vocabularyConfig,
            @RequestParam(defaultValue = DEFAULT_SEVERITY_LEVEL, required = false) String severityLevel) {
LHAMPTON commented 3 years ago

Still doesn't work; Method not allowed.

I've tried several variations of ContentType: multipart/, multipart/form-data, /form-data all fail with the same error: Method not allowed. Even tried substituting use of the headers collection in place of adding them to the connection string.

The calling method looks like this (VB DotNet): (sPath and sFileName are parameters ) Dim sFullName As String = IO.Path.Combine(sPath, sFileName) Dim oFileStream As New FileStream(sFullName, FileMode.Open) Dim sAddress As String = oParams.CDA_ValidatorAddress

' The address is set to: http://localhost:8080/referenceccdaservice

    Dim sCommand As String = sAddress
    '& "?validationObjective=CCDA_IG_Only&" &
    '                                    "referenceFileName=test&" &
    '                                    "ccdaFile=" & sFileName & "&" &
    '                                    "severityLevel=ERROR"

    Dim oRequest As HttpWebRequest = CType(WebRequest.Create(sCommand), HttpWebRequest)
    With oRequest.Headers
        .Add("validationObjective", "CCDA_IG_Only")
        .Add("referenceFileName", "test")
        .Add("ccdaFile", sFileName)
        .Add("severityLevel", "ERROR")
    End With

    oRequest.Method = "POST"
    oRequest.ContentType = "multipart/form-data" 

    oRequest.ContentLength = oFileStream.Length 
    Dim oRequestStream As Stream = oRequest.GetRequestStream
    oFileStream.CopyTo(oRequestStream)
    oRequestStream.Close()

    Try
        Dim oResponse As HttpWebResponse = CType(oRequest.GetResponse, HttpWebResponse)
        Dim oReader As New StreamReader(oResponse.GetResponseStream)
        Dim sResults As String = oReader.ReadToEnd

        MsgBox(sResults, MsgBoxStyle.OkOnly, "CDA Validation Result")

    Catch ex As Exception
        MsgBox(ex.Message, MsgBoxStyle.OkOnly, "CDA Validation Failed")
    End Try
drbgfc commented 3 years ago

Have you tried verifying you have the same issue using software to call the endpoint like PostMan or curl?

The url looks fine. I just happened to host on 8000 as have other things on 8080 but 8080 is fine if you don't.

LHAMPTON commented 3 years ago

Just tried with postman; same result:

image image

drbgfc commented 3 years ago

Have you tried navigating to http://localhost:8080/referenceccdaservice/ui and validating to ensure it works in general?

drbgfc commented 3 years ago

If you can use that, you will know if the service is working at all. And, if not, you can use dev tools to see the network and console tabs to see what might have gone wrong.

drbgfc commented 3 years ago

If that doesn't work, it may be some sort of network or firewall issue? Or something else I don't know without looking into it deeper. Unfortunately, I won't have time to look into it today if so, simply due to the priorities of our contractual obligations. But, you can feel free to keep posting updates.

LHAMPTON commented 3 years ago

That was the first thing I tried after getting the service to run. Just re-checked it and it is still working.

LHAMPTON commented 3 years ago

As for network/firewall issues, I'm sure its neither of those. Each time I post to the validator, the log reports the warning: [PageNotFound:208] Request method 'GET' not supported. After doing the above validation, it has a myriad of validation log entries.

drbgfc commented 3 years ago

If the UI is working, then open the network tab before running and verify the details in which the API was called. Since that is working, you should be able to replicate it via your own call. I would focus on that.

If you've exhausted that option - you could try another WAR from a previous release.

And if beyond that, you could try building your own from source or looking into the source. But I don't think that will help.

Beyond that I would post your full log files and your configuration files (XML) here to see if any issues and I can look at some point in the future. Thanks.

drbgfc commented 3 years ago

btw if doing certification, you should probably be sending a curesUpdate value to true. This won't solve your issue though. This will trigger different content validation. If you aren't using content validation, then it's irrelevant, for now, on your end. On our end it determines what vocabulary is chosen as well, but, for you, that's simply dependent on what you supply locally.

LHAMPTON commented 3 years ago

Sorry, I don't know to what "network" tab you are referring. When I open the validator UI, all I get is a set of options, of which the last one is the Submit button. I've peeked at the html of the validationUI and it appears (though I'm not a web developer) to be instantiating a validationController object, setting values, and invoking a "validate" method to do the validation... not something that I can do via a web POST. I've browsed through the sub-directories of the referenceccdaservice folder and can't correlate anything I see with the objects in the UI's html.

When placing the call, if I add anything after the ".../referenceccdaservice", such as ".../referenceccdaservice/DoValidate", the call fails when I try to copy the file to the request stream; yet it seems like it needs something there (some other command in place of "DoValidate".

drbgfc commented 3 years ago

Look up how to use Chrome developer tools. But, basically, press f12 in Chrome when u are on the web app, before you run it, and it will open, and track events from there. There is a network tab in there, to see details of everything that's going on (requests/responses), including the POST call which is being made by the web app There is also a console tab with logs, if any, form the web app

LHAMPTON commented 3 years ago

Yesterday afternoon, in searching for information on the "form-data" content type, I learned that it required an additional parameter to specify a boundary... which explained the errors I was seeing in the log: the request was rejected because no multipart boundary was found]. After I added a boundary, that error went away.

This morning, I was looking at the Validation UI scenario file choices. The label says that if not using a scenario, select "No scenario File", but there is no such choice. In any case, I tried to use that name for the referenceFileName but it still fails. So...

I stopped the validator service, deleted todays logs and re-started. After it started, I used the UI to validate one of our certification files, which succeeded. Using the newly-found (thanks) Network tab, I can see both the headers and the form data. It appears that the parameters are sent in the data stream, I presume separated by the boundary string, though that detail is not shown in the network window. I have been sending them in the header.

image

Even in the developer tools' network window, Chrome is hiding some of the details such as the exact way that the form data is sent with the boundary. I found a nice article here https://www.paraesthesia.com/archive/2009/12/16/posting-multipartform-data-using-.net-webrequest.aspx/ that explains it nicely, so I'm working on building that into my project.

The only question I have for now is: What exactly do I send for the referenceFileName when I'm performing a validation on a "random" file, such as a file received from another doctor's office? A blank string, the literal value "No scenario File", or something else?

Thanks

jasoncmedent commented 3 years ago

This is how I build my validation POST.

/referenceccdaservice/?validationObjective=C-CDA_IG_Plus_Vocab&referenceFileName=No%20scenario%20File

drbgfc commented 3 years ago

You're welcome. Glad you got it working.

The only question I have for now is: What exactly do I send for the referenceFileName when I'm performing a validation on a "random" file, such as a file received from another doctor's office? A blank string, the literal value "No scenario File", or something `else?

I usually use the string 'test' although from what I remember any name other than an actual matching name will work. But, use test and it will definitely always not match anything. IIRC, the match would be to a scenario file name excluding the version. If the string contains anywhere a match within it to a significant unique portion of the scenario, it will kick off content validation for that scenario.

That's correct that using multipart/form-data requires a boundary to separate the arguments. I wonder why Postman didn't work for you? It doesn't require configuring a boundary manually and I forwarded my exact setup above. This and lack of time is the reason I haven't looked over your VB source code in any detail yet.

drbgfc commented 3 years ago

Since you had some issues with setup. I would like for the documentation to be updated so that future users don't encounter the same issues. Do you have suggestions for what to incorporate? A summary of what was missing of sorts. Or, better yet, maybe you could make a PR to develop? Even if it is just sloppy ideas, that you aren't fully sure of, I can clean it up and finalize form there. If possible, I would definitely appreciate it, as would others. Thanks.

LHAMPTON commented 3 years ago

After making the form-data changes, my first attempt failed with "method not allowed". Since I saw that the UI appeared to be sending the command "referenceccdaservice/", I appended a slash and tried again. Since Chrome indicates that the file is being sent with a mime type of binary, I used a file MIME type of "binary", which failed with 415 "Unsupported Media Type".

Tried again with: application/octet-stream, same response. text/html, same response. text/plain, same response text/xml, same response application/xml, same response

I give up... what's the correct content-type to use within the file's content-disposition section? I am writing the following data to the request stream before sending the file:

Content-Disposition: form-data; name="ccdaFile"; filename="myFileName.xml" CrLf Content-Type: text/xml (or any of the above) CrLf CrLf {file stream content} CrLf followed by other parameters and the final boundary.

I am opening the file with a FileStream object and using its CopyTo method to write it to the request steam.

Each failed attempt generates an entry in the localhost access log like: 127.0.0.1 - - [19/Nov/2020:10:24:42 -0500] "POST /referenceccdaservice/ HTTP/1.1" 415 723

So close...

drbgfc commented 3 years ago

I got confused somewhere. I thought you had it working. If the UI is working, and, If postman is not working, with the configuration I gave, then any attempt to write your own software is a waste of time IMO until you/we figure out why postman is not working, with a working config. As, that would imply the issue is elsewhere. Is that still the case? If however postman is working, then this is an issue of creating the proper source code on your end. Which, I may be able to give tips here and there to some extent.

LHAMPTON commented 3 years ago

I tried again with Postman; failed with "no multipart boundary". Added a Boundary string to the Content-Type header; failed with 400 - bad request.

Since my sending code is failing with "Unsupported media type", as noted above, @drbgfc , can you tell me what media type the service is expecting?

drbgfc commented 3 years ago

I don't understand why it would say that. But it would seem your web service is not working then.

Try the prod server (and disable SSL verification in postman settings tab):

image

Does that work? If that doesn't work, then it's a network/firewall/computer/software/anti-virus/admin management, etc. issue.

The content type for the file is text/xml

Here is a full example for a production call:

POST /referenceccdaservice/ HTTP/1.1
Host: ccda.healthit.gov
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="ccdaFile"; filename="/C:/Users/dbrown/XML_CCDA_Docs/R2PointOne/EF/C-CDA_R2-1_CCD_EF.xml"
Content-Type: text/xml

(data)
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="referenceFileName"

test
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="validationObjective"

C-CDA_IG_Plus_Vocab
----WebKitFormBoundary7MA4YWxkTrZu0gW

If you can get postman working for production, you can then use this to write VB code to access that, knowing it works. Then, if that works, you can swap the URL for your local instance. And, if that doesn't work, you would know the issue is now not the code, but the service.

LHAMPTON commented 3 years ago

Using postman, though it still fails, I found that I had to remove the content-type header item so that it would use the selected form-data option. With that change, for both my local server and the production server, it is returning error 415, which is the same error that my software is getting. Here are the headers I have selected in Postman...

image

And here is the body with response:

image

Are your header items different?

I changed my MIME type back to text/xml but that didn't make a difference.

drbgfc commented 3 years ago

image

Header items above. Hope that helps.

If following that does not work would try installing postman on a personal computer, and running a test to prod from home, if you have not/are not already.

As you likely understand, if you can't get production to work, then the specific issue is completely unrelated to your source code or your WAR.

LHAMPTON commented 3 years ago

Since the validator was working locally using the UI, but not with either Postman or my code, I decided to peek at the actual traffic. After a few changes to some existing code (written for another purpose), I was able to see what the UI was REALLY sending... and found:

Content-Disposition: form-data; name="ccdaFile"; filename="206666_Text.txt"{Cr}{Lf}Content-Type: text/plain{Cr}{Lf}

Also, the production call example contains invalid boundaries; Per the HTML documentation, the header specifies a boundary string, but when it is used with the document it is always preceded by two dashes ("--"), and the trailing boundary must also be suffixed with two dashes.

After changing from Content-Type text/xml to text/plain, the "Unsupported Media Type" error went away and, after some other housekeeping changes, I now have a working validator.

One issue to note is that I left the service running overnight; this morning, my first call to the validator timed out. I checked the logs and found that it had encountered an out-of-memory error, and before I could make anything work I had to stop and re-start it. Since then I have run many of the certification samples through it without any problems. For the benefit of any others who need to make this work in the future, I'll add another comment with my working validation code.

Thanks for all the help.

LHAMPTON commented 3 years ago

As promised, here is the (almost) final copy of the validator interface. Most of our applications are written in Vb.Net, but should be easy enough to translate to C or other languages.

Imports System.IO Imports System.Net Imports System.Text Imports System.Xml Imports MSSEMRData

'''

''' For 2015 certification, we are required to validate incoming CDA files. ''' This class takes the name of the inbound file, loads it, and submits it ''' to the validation service running (usually?) on the local server. If any ''' errors are encountered, saves them to a file that starts with the inbound ''' name and is suffixed by "_Errors".txt. ''' Public Class clsValidateInboundCDA

''' <summary>
''' Loads the file and posts it to the validation service.
''' </summary>
''' <param name="sPath"></param>
''' <param name="sFileName"></param>
Public Shared Sub ValidateCDA(sPath As String, sFileName As String, oParams As clsMeaningfulUseParams)
    ' For development and certification, the negative testing documents are found at: \\GEORGE\Dev\Drummond Group\2015 edition in 2020\2015-certification-ccda-testdata-master From Validator\Receiver SUT Test Data\NegativeTesting_CCDS
    Dim sFullName As String = IO.Path.Combine(sPath, sFileName)
    Dim oFileStream As New FileStream(sFullName, FileMode.Open)
    Dim iFileLength As Integer = oFileStream.Length
    Dim sAddress As String = oParams.CDA_ValidatorAddress  ' The local or remote http address of the validator

    Dim sCommand As String = sAddress & "/"

    Dim sBoundary As String = "----WebKitFormBoundary4gd6jD5r3xwQiY7M"  ' A string of 1 to 70 characters. This value is what I saw that their UI was sending.

    Dim oRequest As HttpWebRequest = CType(WebRequest.Create(sCommand), HttpWebRequest)

    oRequest.Method = "POST"
    oRequest.Accept = "application/json"  ' -- The UI sends "application/json, text/plain, */*" 
    oRequest.ContentType = "multipart/form-data; boundary=" & sBoundary

    ' After using the chrome web developer tools to watch what was being sent from the validator's UI, I discovered that
    ' the parameters were being sent in form data, not in the header, in this order:
    ' validationObjective, referenceFileName, ccdaFile (binary data), severityLevel, curesUpdate.

    ' In order to know the content length for the stream, I pre-code all of the form data other than the actual file.
    Dim oPreFileData As New Text.StringBuilder, oPostFileData As New Text.StringBuilder

    AddBuilderFormData(oPreFileData, "validationObjective", "C-CDA_IG_Plus_Vocab", sBoundary)
    AddBuilderFormData(oPreFileData, "referenceFileName", "Readme.txt", sBoundary)  ' Could use "No scenario File", but this is what their UI sends.

    ' Now for the file header.
    AddBuilderFileName(oPreFileData, "ccdaFile", sFileName, sBoundary)

    ' And the end-of-file and remaining parameters.
    oPostFileData.Append(vbCrLf)  ' Follows the actual file
    AddBuilderFormData(oPostFileData, "severityLevel", "ERROR", sBoundary)
    AddBuilderFormData(oPostFileData, "curesUpdate", "true", sBoundary)
    ' And now for the end-of-it-all boundary
    oPostFileData.Append("--")
    oPostFileData.Append(sBoundary)
    oPostFileData.Append("--")

    oRequest.ContentLength = oPreFileData.Length + iFileLength + oPostFileData.Length
    Dim oRequestStream As Stream = oRequest.GetRequestStream
    Dim aPreFileBytes() As Byte = clsStringUtils.StringToByte(oPreFileData.ToString)  ' This is our string-to-byte-array utilitity, you should be able to use a DotNet native function but we've had issues with it adding extra undesirable "stuff".
    Dim aPostFileBytes() As Byte = clsStringUtils.StringToByte(oPostFileData.ToString)
    Try
        oRequestStream.Write(aPreFileBytes, 0, aPreFileBytes.Length)
        oFileStream.Seek(0, SeekOrigin.Begin)  ' This is only here for debugging - allows us to send multiple requests without needing to re-open the stream.

        Dim iBytes As Integer = iFileLength
        Dim aBuffer(50000) As Byte
        Do
            Dim iRead As Integer = oFileStream.Read(aBuffer, 0, Math.Min(iBytes, 50001))
            oRequestStream.Write(aBuffer, 0, iRead)
            iBytes -= iRead
        Loop While iBytes > 0
        oRequestStream.Write(aPostFileBytes, 0, aPostFileBytes.Length)

    Catch ex As Exception
        MsgBox(ex.Message)
    End Try

    oRequestStream.Close()

    Try
        Dim oResponse As HttpWebResponse = CType(oRequest.GetResponse, HttpWebResponse)
        Dim oReader As New StreamReader(oResponse.GetResponseStream)
        Dim sResults As String = oReader.ReadToEnd

        ' Our code currently targets DotNet 4.7.2, so we use Newtonsoft for processing the response Json.
        Dim oResultDict As Dictionary(Of String, Object) = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Dictionary(Of String, Object))(sResults) 'Converting result string into object.
        If oResultDict.ContainsKey("ccdaValidationResults") Then
            Try

                Dim oValidationResults As Newtonsoft.Json.Linq.JArray = oResultDict.Item("ccdaValidationResults")
                If oValidationResults.Count > 0 Then
                    ' For testing, I show the errors here.  In the live environment, they will be written to a file named {FileName}_Errors.txt
                    Dim oForm As New frmShow_CCDA_ValidationErrors
                    oForm.ShowErrors(sFileName, oValidationResults.ToString)
                End If
            Catch ex As Exception
                MsgBox(ex.Message, MsgBoxStyle.OkOnly, "JSON Result Extraction Error")
            End Try
        End If

    Catch ex As Exception
        MsgBox(ex.Message, MsgBoxStyle.OkOnly, "CDA Validation Failed")
    End Try
    oRequestStream.Dispose()
    oFileStream.Dispose()
End Sub

Private Shared Sub AddBuilderFormData(oBuilder As Text.StringBuilder, sFieldName As String, sFieldValue As String, sBoundary As String)
    ' From the article found at: https://www.paraesthesia.com/archive/2009/12/16/posting-multipartform-data-using-.net-webrequest.aspx/
    oBuilder.Append("--")
    oBuilder.Append(sBoundary)
    oBuilder.Append(vbCrLf)
    oBuilder.Append("Content-Disposition: form-data; name=""" & sFieldName & """")
    oBuilder.Append(vbCrLf)
    oBuilder.Append(vbCrLf)
    oBuilder.Append(sFieldValue)
    oBuilder.Append(vbCrLf)
End Sub

Private Shared Sub AddBuilderFileName(oBuilder As Text.StringBuilder, sFormFileName As String, sActualFileName As String, sBoundary As String)
    oBuilder.Append("--")
    oBuilder.Append(sBoundary)
    oBuilder.Append(vbCrLf)
    oBuilder.Append("Content-Disposition: form-data; name=""" & sFormFileName & """" & "; filename=""" & sActualFileName & """")
    oBuilder.Append(vbCrLf)
    oBuilder.Append("Content-Type: text/plain")
    oBuilder.Append(vbCrLf)
    oBuilder.Append(vbCrLf)
End Sub

End Class

drbgfc commented 3 years ago

You are correct, it is text/plain not text/XML. I had quickly copied the postman output to try and provide you some help while busy elsewhere, which is where that HTML production call POST /referenceccdaservice/ HTTP/1.1 came from. I'm not sure why it (Postman) translated to the request details to text/XML. I just assumed it was correct as it was 'working'. But, of course, text/XML makes no sense as our API does not parse XML for the request. Apologies for not seeing that that was the issue you were having sooner.

I think in general it is assumed the default request would be text/plain, and not specified unless otherwise. Clearly though we should specify it in the readme. As an external user, is there anywhere you think it would be most appropriate? Is there anything else we should add to the readme as per this discussion that was missing for you? So far I am seeing these things but maybe you know more:

-Content-Type -Explain how to access static UI -Maybe provide a postman example

Another thing: You should be able to get postman working without specifying a content-type (then it will default). So, I'm wondering if somewhere you have overridden that with text/XML? Maybe in the headers tab? Or of course you should be able to override the default with text/plain, but, there really wouldn't be any point to that, other than specificity.

Feel free to answer here, or, better yet, submit a PR to the readme on the develop branch for some or all (or more) of these items. I will fill in any blanks if needed.

I wonder if we should also either, add that VB code to the readme in an API section along with other examples, like JavaScript, and Java, and Postman. It's something maybe you could get started, towards the end of the readme, and I could fill in the rest. Thanks for sharing. I haven't seen anyone else try to use VB but in the spirit of open-source that's great to have. Anyway, any help you can offer would be appreciated. Have a good one.