Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot add a header "Content-Type" in v107 #1713

Closed
PusheadSG opened this issue Jan 18, 2022 · 36 comments
Closed

Cannot add a header "Content-Type" in v107 #1713

PusheadSG opened this issue Jan 18, 2022 · 36 comments
Labels

Comments

@PusheadSG
Copy link

Describe the bug
Header "Content-Type" is not getting added to the request

To Reproduce
Make any RestSharp request, and .AddHeader("Content-Type", "blivelsnotch") to the request. Run the request and examine the data being sent. There will be no "Content-Type" header sent, no matter what value you enter. Change the spelling of "Content-Type" to "ContentType" and the header gets added.

Expected behavior
.AddHeader("Content-Type", "value") should add a header of "Content-Type" to the request.

Desktop (please complete the following information):

  • OS: Windows 10
  • .NET 5
  • Version 107.1.1
@PusheadSG PusheadSG added the bug label Jan 18, 2022
@alexeyzimarev
Copy link
Member

I fixed it locally. The issue is caused by the fact that Content-Type is the content header.

Also, please consider this: https://restsharp.dev/support/#content-type

@PusheadSG
Copy link
Author

Thanks - I did note the Content-Type behavior based on what you are sending - when sending a file, will RestSharp add a Content-Type of multipart/form-data to the header when this gets fixed?

@alexeyzimarev
Copy link
Member

alexeyzimarev commented Jan 19, 2022

Yes, but it's not what RestSharp does, it is how MultipartFormDataContent works (and all other derivatives of HttpContent). It already works as it should. What I fixed was adding content headers to the request content. But it is not needed in most cases. If you are sending a file and your request doesn't get the multipart form content type, I would ask you to trace the request using requestbin.com and ensure that's the case, because RestSharp has tests for file uploads, including the content type check.

@PusheadSG
Copy link
Author

Agreed - not needed in most cases, but in this case, it's required - I'm uploading an image to Test Rail's API, and they require the request have Content-Type = multipart/form-data in the header when uploading the file. Why? Heaven only knows, but without it, you can't upload any attachments. I used HttpTracer and see that RestSharp is doing everything correctly in the request body, and it would probably work fine if Test Rail was not requiring what they do in the header, but we have no control over how they implement their restful API.

Thanks a ton for working on this.

@alexeyzimarev
Copy link
Member

What I mean is that RestSharp always sets the content type to multipart form data when you add a file to the request. So, I am not sure what's wrong in your case.

@alexeyzimarev
Copy link
Member

Actually, what I "fixed" was wrong. It was already working correctly. Both default content type and custom content type are correctly set from the beginning. We have a test for multipart form specifically, using a custom content type. After my "fix" it broke down. The published version works as it should. So, I'd need some reproduction as I believe it works correctly.

[Fact]
public async Task MultipartFormData_WithCustomContentType() {
var request = new RestRequest("/", Method.Post);
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets", "TestFile.txt");
const string customContentType = "multipart/vnd.resteasy+form-data";
request.AddHeader(KnownHeaders.ContentType, customContentType);
request.AddFile("fileName", path);
request.AddParameter(new BodyParameter("controlName", "test", "application/json"));
string boundary = null;
request.OnBeforeRequest = http => {
boundary = http.Content!.GetFormBoundary();
return default;
};
var response = await _client.ExecuteAsync(request);
var expectedFileAndBodyRequestContent = string.Format(ExpectedFileAndBodyRequestContent, boundary);
var expectedCustomMultipartContentType = string.Format(ExpectedCustomMultipartContentType, boundary);
response.Content.Should().Be(expectedFileAndBodyRequestContent);
RequestHandler.CapturedContentType.Should().Be(expectedCustomMultipartContentType);
}

@alexeyzimarev
Copy link
Member

Again, with AddFile you should not set the multipart form data content type manually, as I mentioned in the docs.

@PusheadSG
Copy link
Author

PusheadSG commented Jan 19, 2022

Here's my code that is making the file upload request:

              Request = new RestRequest("/index.php?/api/v2/add_attachment_to_result/122")
                                    //.AddHeader("Content-Type", "multipart/form-data")
                                    .AddFile("attachment", Screenshot);

               RestResponse AddAttachmentResponse = Client.PostAsync(Request).GetAwaiter().GetResult();

Here's the HttpTracer log content:

==================== HTTP REQUEST: [POST] ====================
POST https://foundationsource.testrail.io/index.php?/api/v2/add_attachment_to_result/122
Authorization: Basic Snipped
Accept: application/json, text/json, text/x-json, text/javascript, *+json, application/xml, text/xml, *+xml, *
User-Agent: RestSharp/107.1.1.0
Cookie: tr_session=4f0d86da-7b93-4ba7-9842-a29e3cc1f58c
--a9439ed1-e7ac-43e9-9a0d-ec270119cade
Content-Type: application/octet-stream
Content-Disposition: form-data; name=attachment; filename=Untitled.png; filename*=utf-8''Untitled.png

?PNG
→
IHDR

☻☻PX?☺sRGB??∟?♦gAMA??♂?a♣       pHYs♫?♫?☺?o?d↑IDAT(Sc?_??Ai∟`dJ30lH☻♀Z↔T☼IEND?B`?
--a9439ed1-e7ac-43e9-9a0d-ec270119cade--

There is a Content-Type in the body "application/octet-stream" but no Content-Type in the header.

@PusheadSG
Copy link
Author

PusheadSG commented Jan 19, 2022

Here's a sample where I'm using AddJsonBody:

RestRequest Request = new RestRequest("/ws/rest/dev/session") 
    .AddHeader("Accept", "application/vnd.api+json")
    .AddJsonBody(Creds, "application/vnd.api+json");

RestResponse r = await Client.PostAsync(Request);

Here's the HttpTrace output. Note, no Content-Type in the header:
==================== HTTP REQUEST: [POST] ====================
POST https://online.qa1.fs.local/ws/rest/dev/session
Accept: application/vnd.api+json
User-Agent: RestSharp/107.0.3.0
{"data":{"type":"session","id":null,"attributes":{"auth-id":0,"auth-name":"UserNameHere","user-id":0,"name":null,"status":null,"role-id":0,"ext-id":0,"password":"NoPasswordHere"}}}

@alexeyzimarev
Copy link
Member

Yeah, I have seen it too. But the header is set, believe it or not.

The reason that you don't see it is that HttpTracer only looks at request headers, but not at content headers. So, headers like Content-Type, Content-Encoding, Content-Length, Content-Disposition are all not there, but there are part of the request.

THAT'S WHY (sorry) I asked to check with requestbin.com.

@PusheadSG
Copy link
Author

I'll see if I can get requestbin set up

@alexeyzimarev
Copy link
Member

It's an online service.

@alexeyzimarev
Copy link
Member

Also, it's easier to set the AcceptedContentTypes property for the client instead of adding the Accept header all the time.

Your JSON request looks totally legit, I hope it works.

@PusheadSG
Copy link
Author

The JSON is working fine - I'm just going to have to spend time is getting a requestbin integration up - I'm in over my head there...

@alexeyzimarev
Copy link
Member

You don't need to do anything except clicking the "Create bin" button on https://requestbin.com and changing the URL for your request to the one they give you. You make a call, they show you everything.

@PusheadSG
Copy link
Author

Well, they are showing up in requestbin, but the boundary parameter is different between v106 (where this worked) and v107 - could that be the problem with the Test Rail API?

content-type
multipart/form-data; boundary="b24c07c9-4fac-411e-a107-df35b59fe1ea"
host:
enzadciy9jbbxv.m.pipedream.net
user-agent:
RestSharp/107.1.1.0

content-type
multipart/form-data; boundary=---------77FE1D56-3833-4505-8367-511BF206C496
host:
enzadciy9jbbxv.m.pipedream.net
user-agent:
RestSharp/106.15.0.0

@PusheadSG
Copy link
Author

Alexey - I sincerely appreciate your help on this, but I'm giving up. I can see no difference in requestbin between 106 and 107, although I suppose it could be the way the file is sent in the form data (since I don't see the actual file stream in requestbin, or, maybe I don't know how to see it there...) The folks at Test Rail have been zero help ("Well, it works with cUrl, so we know it's working...") So, if someday you are bored and want to pick this up, I'll gladly jump in and help, best as I can, but until then, I'm closing this.

Thanks again for your help.

@alexeyzimarev
Copy link
Member

I feel you here. Have you tried with curl btw? You see for yourself that requests are nearly identical. The issue here that most of the people would say "I will use HttpClient" but that's exactly what RestSharp is using. Of course, it could be possible to replace the form boundary as well, but I just don't understand what the issue is.

@alexeyzimarev
Copy link
Member

Thanks for the coffee :) If you manage to get the curl request to work and trace it via requestbin, I might be able to add some override function for the form boundary. It's already working when you use a custom content type, I extract the boundary and replace it, so it might work using a function to format the whole thing in a different way.

@PusheadSG
Copy link
Author

PusheadSG commented Jan 20, 2022

You are very welcome. I've actually gotten the request to work with v106, JMeter, and a Powershell script, so I do know it works. If you want to set up a requestbin and give me the URL, I'll post from v106, JMeter and powershell, then post from v107 and you can see for yourself. It's not the boundary, as I downloaded the RestSharp source code, and in the debugger, changed the boundary parameter, deleting the quotes, and Test Rail still barfed. The only thing left that I don't know how to check is the actual file contents that get sent.

@alexeyzimarev
Copy link
Member

alexeyzimarev commented Jan 20, 2022

Try setting the new property:

request.FormatMultipartContentType = (ct, boundary) => $"{ct}; boundary=---------{boundary}";

using the latest preview.

@PusheadSG
Copy link
Author

I got the latest preview and made that change, but requestbin is showing multipart/form-data; boundary="c973ce2e-fbc8-420e-8939-2e8cf45d7393" - ???

@PusheadSG
Copy link
Author

107.1.2-alpha.0.7

@alexeyzimarev
Copy link
Member

The version is correct, but it was very late, so I haven't added any tests. Will try testing it later today.

@PusheadSG
Copy link
Author

Let me know when I can help

@alexeyzimarev
Copy link
Member

I added a test and found the issue. The code I added was only triggering if there's a content type header parameter added to the request. I changed it now, the 0.7 alpha should work.

@alexeyzimarev
Copy link
Member

But then again, it's a hypothesis that this is the only thing that makes your request break...

@PusheadSG
Copy link
Author

Well, this is interesting - with the new boundary, their server is throwing an internal server error...

@PusheadSG
Copy link
Author

PusheadSG commented Jan 21, 2022

If I do this: Request.FormatMultipartContentType = (ct, boundary) => $"{ct}; boundary={boundary}" I go back to the bad request, but if I have the leading hyphens (Hex 2d) it's an internal server error...

@alexeyzimarev
Copy link
Member

I think it's something else. I looked at this https://github.com/gurock/testrail-api/blob/master/dotnet/Gurock/TestRail/APIClient.cs#L122 and it seems that they use the same boundary style (string in quotes). For some reason, they use the file extension as content type of the file.

Atm I don't think it's about the form data itself, but the form content. For example, they might fail to parse content disposition properly.

It's your local instance as far as I understand, is there anything in the logs for this server internal error?

@alexeyzimarev
Copy link
Member

alexeyzimarev commented Jan 22, 2022

I think RestSharp.107.1.2-alpha.0.9 would work for you, it seems to be the same issue as #1715

@PusheadSG
Copy link
Author

Yes! It's working! Thanks for your tenacity!

@alexeyzimarev
Copy link
Member

Nice. It means that you don't need to override the form boundary format, and I might remove this property as it seems to be useless. During the research of #1715 I found out that both the filename and the name parameters in Content-Disposition need to be in quotes and it seems most or all of the issues related to uploads are resolved by the last few changes.

@PusheadSG
Copy link
Author

Fantastic. Again, thanks for sticking to it on this one.

@alexeyzimarev
Copy link
Member

No worries, glad to help. It seems like the file upload issue was one of the very few that caused so many issues, so I am happy to get it resolved.

One thing, could you please ensure it works without custom form boundary format? I already removed the property, but haven't pushed. Just want to be sure I don't break anything.

@PusheadSG
Copy link
Author

Works perfectly without the custom boundary value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants