How to call methods after HttpResponse? - c#

I am a bit puzzled at the moment. I have a web application that manipulates a file and then returns the file to a user's browser for download when it's done.
The download part is going well, as I'm using Response.AddHeader and Reponse.BinaryWrite to push the file back to the browser but I am unable to call any further methods after using Response methods.
I suppose I have not worked with HttpReponse enough to know the trick to this. Perhaps I would be better off using another class or generic handler to handle the download?
My code goes something like...
// Methods to be called first
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}.pdf", "New_Merged_PDF_" + DateTime.Now.ToString("MMMM-dd-yyyy")));
Response.ContentType = "application/pdf";
Response.BinaryWrite(output.ToArray());
Response.End();
// Methods to be called last (these wont work)
Probably something simple that I'm overlooking but I'm still trying to figure it out.

To add a little color to Servy's explaination; there is an order of operations within the HTTP protocol specification. One of them is that the Headers need to be sent to the client before the Body. This allows the receiver of the response to properly deal with the Body that is sent based on any Headers.
The presence of a message-body in a request is signaled by the
inclusion of a Content-Length or Transfer-Encoding header field in
the request's message-headers.
IETF RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1
One of the few (if the only, I'm not sure) exception is the Content-Type: multipart/mixed;
IETF RFC 1867 - Form-based File Upload in HTML (although Obsolete, examples are still relevant)
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"
Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--

Related

Sending/Uploading Files through APIs - .Net Core

I would like to send a file through an API. The server will receive the file and save it on the server drive.
The question is that I have two Options to do this:
Read the file as a string and send the whole string in the request Body
Use multi-part
Could someone help in the pros and cons in using the two options.
It's not related to pros and cons, So basically to understand what is multipart means
Multipart requests combine one or more sets of data into a single
body, separated by boundaries. You typically use these requests for
file uploads and for transferring data of several types in a single
request (for example, a file along with a JSON object).
This mean that multipart contains different section of info that separated by boundary(random number).
For example:
POST /upload HTTP/1.1
Content-Length: 428
Content-Type: multipart/form-data; boundary=abcde12345
--abcde12345
Content-Disposition: form-data; name="id"
Content-Type: text/plain
{...Additional plain content goes here...}
--abcde12345
Content-Disposition: form-data; name="address"
Content-Type: application/json
{
"street": "3, Garden St",
"city": "Hillsbery, UT"
}
--abcde12345
Content-Disposition: form-data; name="profileImage "; filename="image1.png"
Content-Type: application/octet-stream
{...file content...}
--abcde12345--
In this example you can see different content sent in one request separated by boundary [boundary=abcde12345]. (Plain text, Json Object and File Content)
The file content section here is used for sending file data in application/octet-stream (string in the binary or base64 format), so basically you are sending the file data in form of string :) as you referred to the first point, but you can include some additional info, may be the new file name to be saved , the user who uploaded this file or whatever you want.
Hope you get the point.
Ref:
https://swagger.io/docs/specification/describing-request-body/file-upload/
https://swagger.io/docs/specification/describing-request-body/multipart-requests/
The only difference on the protocol level is that multipart/form-data requests must adhere to RFC 2388 while a custom typed request body can be arbitrary.
Using Base64 string has an advantage for uploading very tiny individual images. It's easy to handle and avoids dependency on Http.IFormFile. On the contrary, base64 encoded files are larger than the original and you need decode it on the server side.

Upload file in server with a multipart/form-data - C#

I want to upload a file in WindChill (is a PLM from PTC). They give to us an REST API with the services to do this. They split an upload of file in 3 stages.
Stage 1 - We call a service where we give the number of files to upload. In this case only one.
Stage 2 - A multipart/formdata where we give the file to upload.
Stage 3 - The last stage where we give the file name, the file size etc...
I think my problem is on stage 2.
All the stages run successfully but when i try to open the uploaded file, in this case a pdf, the file is blank, but with the same number of pages of the original one. I compare the content of the uploaded file with the original one and the content inside is the same with a big difference. The original is with an ANSI encoding while the uploaded one is with the UTF-8 encoding. So, I think my problem is on the stage 2.
I'm with some doubts on this stage. In C# I get the bytes[] of file, but in the end I need to pass this bytes to a string to send in a multipart form. What is the encoding that i should use to get string? I tested with default, UTF-8, UNICODE, ASCII encoding but nothing.
Here is the example of the Post request body. In a C# I use the HTTPWebRequest to make a request.
------boundary
Content-Disposition: form-data; name="Master_URL"
https://MyUrl/Windchill/servlet/WindchillGW
------boundary
Content-Disposition: form-data; name="CacheDescriptor_array"
844032:844032:844032;
------boundary
Content-Disposition: form-data; name="844032"; filename="newDoc.pdf"
Content-Type: application/pdf
%PDF-1.7 //// The content of the file starts here
%ยตยตยตยต
1 0 obj
........
------boundary--
Before this approach I tried to convert the bytes[] ToBase64String and send an body like this:
------boundary
Content-Disposition: form-data; name="Master_URL"
https://MyUrl/Windchill/servlet/WindchillGW
------boundary
Content-Disposition: form-data; name="CacheDescriptor_array"
844033:844033:844033;
------boundary
Content-Disposition: form-data; name="844033"; filename="newDoc.pdf"
Content-Type: application/pdf
Content-Transfer-Encoding: base64
JVBERi0xLjcNCiW1tbW1DQox ........ //// The content of the file starts here
------boundary--
In this case, when I try to open the file i get the error "Failed to load PDF document". The file is corrupt.
I think the problem is on the stage 2, but I will share the body that i send in last stage for your understanding.
{"ContentInfo":[{"StreamId":844034,"EncodedInfo": "844034%3A40384%3A9276564%3A844034","FileName": "newDoc.pdf","PrimaryContent": true,"MimeType" : "application/pdf","FileSize" : 40384}]}
The StreamId and the EncodedInfo are returns of the stage 2 that I need to provide in the stage 3.
Anyone can see what I'm doing wrong? Anyone have some tips to help me to solve this issue?
Many thanks.
I have a big tip for solve issues like that.
Use postman. Make all the job in postman. After your job is done, you can generate the code for multiple languages with postman. Many thanks.

Posting `multipart/form-data` with Flurl

I am in need to post the following request:
POST http://target-host.com/some/endpoint HTTP/1.1
Content-Type: multipart/form-data; boundary="2e3956ac-de47-4cad-90df-05199a7c1f53"
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Content-Length: 6971
Host: target-host.com
--2e3956ac-de47-4cad-90df-05199a7c1f53
Content-Disposition: form-data; name="some-label"
value
--2e3956ac-de47-4cad-90df-05199a7c1f53
Content-Disposition: form-data; name="file"; filename="my-filename.txt"
<file contents>
--2e3956ac-de47-4cad-90df-05199a7c1f53--
I can do this really easily with Python requests library as follows:
import requests
with open("some_file", "rb") as f:
byte_string = f.read()
requests.post(
"http://target-host.com/some/endpoint",
data={"some-label": "value"},
files={"file": ("my-filename.txt", byte_string)})
Is there any way to do the same with the Flurl.Http library?
My problem with the documented way of doing it is that it will insert the Content-Type header for each key-value pair and it will insert the filename*=utf-8'' header for the file data. The server I am trying to post the request to, however, does not support this. Also note the double quotes around the name and filename values in the headers.
EDIT: Below is the code I used to make the post request with Flurl.Http:
using System.IO;
using Flurl;
using Flurl.Http;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
var fs = File.OpenRead("some_file");
var response = "http://target-host.com"
.AppendPathSegment("some/endpoint")
.PostMultipartAsync(mp => mp
.AddString("some-label", "value")
.AddFile("file", fs, "my-filename.txt")
).Result;
}
}
}
According the spec (dated June 2011), sending both filename and filename* is recommended for maximum compatibility:
Many user agent implementations predating this specification do not
understand the "filename*" parameter. Therefore, when both "filename"
and "filename*" are present in a single header field value, recipients
SHOULD pick "filename*" and ignore "filename". This way, senders can
avoid special-casing specific user agents by sending both the more
expressive "filename*" parameter, and the "filename" parameter as
fallback for legacy recipients.
If filename* is actually causing the call to fail, there's a real problem with the server adhering to the HTTP spec. Also, enclosing name and filename in quotes is very non-standard.
That said, Flurl's shortcuts cover the 90% cases, but you can always use the underlying HttpClient APIs to cover unusual cases like this one. In this case I think you need to build up the content manually so you can deal with those Content-Disposition headers:
var mpc = new MultipartContent();
var sc = new StringContent("value");
sc.Headers.Add("Content-Disposition", "form-data; name=\"some-label\"");
mpc.Add(sc);
var fc = new StreamContent(fs);
fc.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"my-filename.txt\"");
mpc.Add(fc);
Then you can use it with Flurl like this:
var response = await "http://target-host.com"....PostAsync(mpc);

Accessing headers of body part in multipart mail in Outlook using C#

I am trying to extract the headers of the body part of a multipart mail message in Outlook. The raw message (which I have not been able to get from my code) looks something like this:
Return-Path: ...
Received: ...
From: ...
Content-Type: multipart/signed; boundary="Apple-Mail=_06FDFEBB-366E-4B1E-AA7F-F5DDEC13FD03"; protocol="application/pgp-signature"; micalg=pgp-sha512
Subject: ...
Message-Id: ...
Date: ...
To: ...
Mime-Version: ...
X-Mailer: ...
--Apple-Mail=_06FDFEBB-366E-4B1E-AA7F-F5DDEC13FD03
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=us-ascii
...
--Apple-Mail=_06FDFEBB-366E-4B1E-AA7F-F5DDEC13FD03
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename=signature.asc
Content-Type: application/pgp-signature;
name=signature.asc
Content-Description: Message signed with OpenPGP using GPGMail
...
--Apple-Mail=_06FDFEBB-366E-4B1E-AA7F-F5DDEC13FD03--
I have replaced some of the none relevant parts by dots. The headers I am trying to get are the ones under the first boundary. So this is the part I am looking for:
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=us-ascii
However, if I could get the entire part between the boundaries, that would also be fine as I could parse it myself.
So far, I have only been able to get the headers at the top of the message (so from Return-Path until X-Mailer).
I was able to do that using a `PropertyAccessor in the following way:
mailItem.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E")
In this case mailItem is my Microsoft.Office.Interop.Outlook.MailItem instance.
So, what my question comes down to: How can I get the headers under the first boundary, or any bigger part of the message that contains them?
For signed messages, Outlook preserves the signed message body (with full MIME data) in an attachment called smime.p7m (it's always called smime, even if it's actually PGP/MIME). Unfortunately, Outlook hides this from you, transparently unpacking the signed message and displaying it instead. There's no way, using the Outlook Object Model, to get the actual message body.
However, if you're willing to call MAPI directly (easiest from native code, but can be done from .NET if you're not afraid of some nasty COM interop), you can get the multipart/signed body - both the signature and the complete signed part - as follows:
Starting from the Outlook MailItem, get the MAPIOBJECT property. This is actually a MAPI IMessage. Cast it to an IMAPISecureMessage (.NET will handle this as a QueryInterface behind the scenes). Call GetBaseMessage() on this IMAPISecureMessage (the only documented function), which returns another IMessage. This is the "real" message, the one with the smime.p7m attachment. Unfortunately, there's no way to put this back into OOM, so you have to continue using MAPI. By calling the functions on IMessage, you can get the attachment, then get its data. You'll need to parse the MIME parts, at least enough to get the signed part without its headers, outer boundaries, or of course the signature part. Verify that signed part (without decoding its internal parts, if any, or decoding quoted-printable or anything like that) against the signature blob.
PR_TRANSPORT_MESSAGE_HEADERS property is the only thing you can get. Outlook does not store the full MIME source of the original message.
You can see what is available in OutlookSpy (I am its author) - click IMessage button.

Can't Programatically Upload csv File with WebClient .Net 4.0

I am attempting to upload a csv file but I can't seem to get it to work programatically. If I use Postman in Chrome to send the file it works and here is what it sends (Fiddler output):
------WebKitFormBoundary2YsMyLR3QAPruTy4
Content-Disposition: form-data; name="Content-Type"; filename="613022.csv"
Content-Type: application/vnd.ms-excel
// File Content here
------WebKitFormBoundary2YsMyLR3QAPruTy4--
However, using this code:
WebClient wc = new WebClient();
wc.Credentials = new NetworkCredential(user, pw);
wc.Headers[HttpRequestHeader.Cookie] = "OBBasicAuth=fromDialog";
wc.Headers[HttpRequestHeader.ContentType] = "application/vnd.ms-excel";
wc.UploadFile(baseURL + service + apiVersion + resource, "post", file);
Results in (Fiddler output):
-----------------------8d101dbe85fe96c
Content-Disposition: form-data; name="file"; filename="613022.csv"
Content-Type: application/vnd.ms-excel
// File Content here
-----------------------8d101dbe85fe96c--
Which does not work and the server returns a 503 error. The only difference I see is in the Content-Disposition name. How can I set this or is there a better way to accomplish this?
Perhaps you need "upload" instead of "post" in the method parameters - just guessing.
I believe the problem with your original approach is that WebClient.UploadFile generates the multipart/form-data request in a way that is unexpected by the server, hence the 5xx error code.
After looking around for a bit, I think the answer to this question should give you a starting point to tweak the request according to your needs.

Categories