C# `[FromBody]` attribute bad unicode decoding for JSON - c#

I have the following code,
[HttpPost("")]
public async Task<IActionResult> Create([FromBody] JToken body)
{
return Json(body);
}
I tried POSTing with and without unicode character, both responds with the exact document, but the unicode ones doesn't decode correctly in C# debugger (it does not display correctly), nor does it do anything useful (.ToString() prints nothing.)
My Request:
POST /api/vendors HTTP/1.1
Content-Type: application/json; charset=utf-8
Host: localhost:33000
Connection: close
User-Agent: Paw/2.2.5 (Macintosh; OS X/10.12.4) GCDHTTPRequest
Content-Length: 18
{
"A":"你好"
}
My Response:
{"A":"你好"}
Under Visual Studio, it shows:

Related

Unsupported Media Type when consuming text/plain

I receive the following response when trying to consume text/plain:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
"title": "Unsupported Media Type",
"status": 415,
"traceId": "|b28d0526-4ca38d2ff7036964."
}
Controller definition:
[HttpPost]
[Consumes("text/plain")]
public async Task<IActionResult> PostTrace([FromBody]string body)
{ ... }
HTTP message:
POST /api/traces HTTP/1.1
Content-Type: text/plain
User-Agent: PostmanRuntime/7.19.0
Accept: */*
Cache-Control: no-cache
Postman-Token: 37d27eb6-92a0-4a6a-8b39-adf2c93955ee
Host: 0.0.0.0:6677
Accept-Encoding: gzip, deflate
Content-Length: 3
Connection: keep-alive
I am able to consume JSON or XML just fine. What am I missing?
Reference: Accepting Raw Request Body Content in ASP.NET Core API Controllers:
Unfortunately ASP.NET Core doesn't let you just capture 'raw' data in any meaningful way just by way of method parameters. One way or another you need to do some custom processing of the Request.Body to get the raw data out and then deserialize it.
You can capture the raw Request.Body and read the raw buffer out of that which is pretty straight forward.
The easiest and least intrusive, but not so obvious way to do this is to have a method that accepts POST or PUT data without parameters and then read the raw data from Request.Body:
[HttpPost]
[Route("api/BodyTypes/ReadStringDataManual")]
public async Task<string> ReadStringDataManual()
{
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
return await reader.ReadToEndAsync();
}
}
Request:
POST http://localhost:5000/api/BodyTypes/ReadStringDataManual HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/plain
Host: localhost:5000
Content-Length: 37
Expect: 100-continue
Connection: Keep-Alive
Windy Rivers with Waves are the best!

INVALID_REQUEST_PARAMETER on DocuSign.eSign.Api.EnvelopesApi

I am experiencing the exact same error as described in question INVALID_REQUEST_PARAMETER on listStatus
However, unlike that OP, I am not using the REST API directly, but am using the C# SDK from https://www.nuget.org/packages/DocuSign.eSign.dll
It would appear that the SDK wrapper is not including the querystring parameters as described by the answer in the above linked post. Is there a workaround other than waiting for DocuSign to fix their SDK -- and where is the appropriate place to submit a bug for their SDK?
Per comment, here is a code sample:
var envelopesApi = new DocuSign.eSign.Api.EnvelopesApi();
var envelopeIds = incentivesWithPendingOffers.Select(i => i.new_OfferLetterEnvelopeID).ToList();
var envelopeInfos = await envelopesApi.ListStatusAsync(_tokenAccountId, new EnvelopeIdsRequest(envelopeIds), null);
Running fiddler to capture the outbound REST call being made by the SDK, I see this:
PUT https://demo.docusign.net/restapi/v2/accounts/[ REDACTED ]/envelopes/status HTTP/1.1
X-DocuSign-SDK: C#
Authorization: Bearer [ REDACTED ]
Accept: application/json
User-Agent: Swagger-Codegen/2.1.0/csharp
Content-Type: application/json
Host: demo.docusign.net
Content-Length: 96
Accept-Encoding: gzip, deflate
{"envelope_ids":["1d324bac-60ea-44b5-9b60-a5de14af3beb","5431d728-4918-4218-9c12-765b1c914724"]}
which returns the following response (which the SDK turns into a .NET Exception):
HTTP/1.1 400 Bad Request
Cache-Control: no-cache
Content-Length: 238
Content-Type: application/json; charset=utf-8
X-DocuSign-TraceToken: [ REDACTED ]
Date: Wed, 01 Aug 2018 20:43:58 GMT
Strict-Transport-Security: max-age=31536000; includeSubDomains
{
"errorCode": "INVALID_REQUEST_PARAMETER",
"message": "The request contained at least one invalid parameter. Query parameter 'from_date' must be set to a valid DateTime, or 'envelope_ids' or 'transaction_ids' must be specified."
}
When the previous answer was written, the SDK didn't support putting the list of envelope IDs in the call body. As of client version 3.1.3 this is now available.
List<string> envelopeIds = new List<string>();
envelopeIds.Add("2b62eb63-784a-4228-be02-876762ea6661");
envelopeIds.Add("406a9a15-c8e9-4227-8dd2-bd9a5318d4fd");
EnvelopeIdsRequest envelopeIdsRequest = new EnvelopeIdsRequest();
envelopeIdsRequest.EnvelopeIds = envelopeIds;
ListStatusOptions options = new ListStatusOptions();
options.envelopeIds = "request_body"; //the Options value controls the query string parameter
EnvelopesInformation envelopesInfo = envelopesApi.ListStatus(accountId, envelopeIdsRequest, options);
I wasn't able to use the envelope_ids=request_body parameter via the SDK, but I was able to get status of several envelopes at once. This would be a viable workaround as long as you're not requesting so many envelope IDs that the URL overflows.
EnvelopesApi.ListStatusChangesOptions lsco = new EnvelopesApi.ListStatusChangesOptions
{
envelopeIds = "fdd1122a-9c1b-4eef-9e24-25bb2cdf2eb2, fe1cb500-6a4c-4328-bf24-55806434852f, 5b1d3828-f8cd-4bba-87f0-538cb920db96"
};
EnvelopesInformation listStatusChanges = envelopesApi.ListStatusChanges(accountId, lsco);
results in an API call to
GET https://demo.docusign.net/restapi/v2/accounts/{{accountId}}/envelopes?envelope_ids=fdd1122a-9c1b-4eef-9e24-25bb2cdf2eb2%2C%20fe1cb500-6a4c-4328-bf24-55806434852f%2C%205b1d3828-f8cd-4bba-87f0-538cb920db96

Content type in call to web service

Please correct me if I'm wrong in defining contet type:
Content-Type: application/json; charset=utf-8
{"my_id":"975","my_a_id":"Test66","param":"4","amount":"66"}
and
Content-Type: application/x-www-form-urlencoded
my_id=123&my_a_id=Test66&param=4&amount=66
What other content types might be?
What content type supports RESTLet NetSuite services?
I have POST to web service:
POST https://some.netsuite.uri?deploy=1 HTTP/1.1
Authorization: NLAuth nlauth_account=3624292_SB3, nlauth_email=xduh31#gmail.com, nlauth_signature=Pass987654, nlauth_role=3
Accept: application/json
Content-Type: application/json; charset=utf-8
Host: rest.eu1.netsuite.com
Cookie: JSESSIONID=**************************************; lastUser=1234567_SB9_1282_3; NS_ROUTING_VERSION=LAGGING; NS_VER=2017.1.0
Content-Length: 69
Expect: 100-continue
{"my_id":"975","my_a_id":"Test66","param":"4","amount":"66"}
Is it good that in URL I have something like x-www-form-urlencoded - ?deploy=1
https://some.netsuite.uri?deploy=1
and data is json style:
{"my_id":"975","my_a_id":"Test66","param":"4","amount":"66"}
UPD
I got answer that I suppose should return JSON data:
HTTP/1.1 200 OK
Date: Sat, 03 Jun 2017 06:21:46 GMT
Server: Apache
Cache-Control: No-Cache
Pragma: No-Cache
Content-Length: 41
Expires: 0
Edge-Control: no-store
X-N-OperationId: 486c2d20-099d-446b-9788-4816db59a1fd
Set-Cookie: .......................; path=/
NS_RTIMER_COMPOSITE: 1688996695:706172746E6572733030312E70726F642E6475622E6E65746C65646765722E636F6D:80
P3P: CP="CAO PSAa OUR BUS PUR"
Vary: User-Agent
Content-Type: application/json; charset=utf-8
org.mozilla.javascript.Undefined#54b896b0
But this answer not looks like JSON?
One problem might be your URL. Have you tried changing
https://some.netsuite.uri&deploy=1
to
https://some.netsuite.uri?deploy=1
Edit:
Also, your Accept header says you are looking for an application/json response, is that what the server is returning?
You can find supported content types in NS Help Center:
RESTlets support JSON and plain text content types for input and
output. For each RESTlet, output content type is the same as input
content type.
You must set the content type in the HTTP Content-Type header. You can
use the following values to specify the input/output content type for
a RESTlet:
application/json
text/plain
If you specify a content type other than JSON or text, a 415 error is
returned with the following message:
Invalid content type. You can only use application/json or text/plain
with RESTlets.
Documentation
Besides that the issue in the url mentioned by shazmodan, the script Id is missing. Below is an example of a valid restlet url:
https://rest.netsuite.com/app/site/hosting/restlet.nl?deploy=1&script=1
You can pass additional parameters in the url:
https://rest.netsuite.com/app/site/hosting/restlet.nl?deploy=1&script=1&my_id=123&my_a_id=Test66&param=4&amount=66
or in the request body if you are using POST and PUT methods but it needs to be a valid JSON. But the Content Type must always be application/json or text/plain.

100K sized POST requests don't get to webAPI?

I'm using Fiddler (or post manager) to invoke requests to my WebApi. ( it's hosted as an ASP.net application in IIS)
This is the service :
[HttpPost]
[ActionName("uploadRessources")]
[AllowAnonymous]
public HttpResponseMessage uploadRessources(ResourcesJson json)
{
...
return Request.CreateResponse(HttpStatusCode.OK, result);
}
For short length body( 3 rows of data) length post data requests like :
POST http://something.com/api/services/uploadRessources HTTP/1.1
Cache-Control: no-cache
Connection: keep-alive
Pragma: no-cache
Accept: application/json
Accept-Encoding: gzip
Accept-Language: he-IL
User-Agent: Mozilla/5.0
Content-Length: 451
Content-Type: application/json
Host: es.com
{ "l":
[{"MasterEntity":2,"screen":"ConfirmHealthDetailsPage","Lbl":"ApproveTheFollowingDetails","enus":"Approve the Following Details:","device":"mobile","description":"NULL"},
{"MasterEntity":2,"screen":"ConfirmHealthDetailsPage","Lbl":"PersonalDetails","enus":"Personal Details","device":"mobile","description":"NULL"},
{"MasterEntity":2,"screen":"FingerPrintResources","Lbl":"CANCEL","enus":"CANCEL","device":"mobile","description":"NULL"}]
}
I DO get a successful response :
HTTP/1.1 200 OK
But for a long body length request ( 101K ) -
It is stuck (it even doesn't hit the breakpoint in my code , while in short request - it does )and I never see a response :
BTW - If later I do run again the short body length request( while still waiting for the previous large requests) - I do get 200 ( for the short length request).
In web.config I did set :
<httpRuntime enableVersionHeader="false" executionTimeout="100000000" maxRequestLength="999999999" />
In IIS : No requests filters
In Event Viewer - I don't see any exceptions or warnings
IIS version : 6.1 ( windows 7) - but it also happens at our server.
Question
Why doesn't my 101k length request - get to my webapi ?
Edit
I've found that it happens for >65k requests. Still don't know what is the problem
It's seems to be an issue with your serializer, tries to put a breakpoint there and see if hits it. If you are not using a custom serializer, creates one temporally just to see what's happen with your request.
It was stuck on request.Content.ReadAsStringAsync().Result ( and never released). Strange but setting it to async , solved the problem. I still don't know why. In General this is the solution - the left pane was the problematic one while the right pane is the working one. ( I must say that the left pane used to work , but after upgrading to 4.6.2 - it started doing problems) - Again - I don't know why

Does it matter that a servicestack.net OPTIONS request returns a 404?

I'm using the method described at https://github.com/ServiceStack/ServiceStack/wiki/New-Api to enable CORS. This seems to work, i.e. I get the correct Access-Control headers back in the response, but the status code is a 404. Does this matter?
For completeness I'd prefer to return a non-error code. Reading http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html it seems as though I ought to be returning a 200. What's the best way to do this?
The service method definition is:-
[EnableCors]
public void Options(ApiKeyRequest apiKeyRequest)
{
}
The HTTP request is:-
OPTIONS http://myreallycoolwebapi/apikeyrequests HTTP/1.1
Host: myreallycoolwebapi
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:8000
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11
Access-Control-Request-Headers: origin, content-type, accept
Accept: */*
Referer: http://localhost:8000/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
and the response is:-
HTTP/1.1 404 Not Found
Server: nginx
Date: Sat, 17 Nov 2012 18:06:01 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 3
Connection: keep-alive
Cache-Control: private
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type
404
UPDATE 1
This is my resource type.
[Route("/apikeyrequests", "POST")]
public class ApiKeyRequest : IReturn<ApiKeyRequest>
{
public string EmailAddress { get; set; }
}
The POST requests work just fine.
I still haven't seen anything matching the expected route /apikeyrequests.
Do you have a custom route definition, e.g:
[Route("/apikeyrequests")]
public class ApiKeyRequest {}
Added for the request?
The CORS spec itself explicitly indicates that a non-200 preflight response is an error. See the "If the response has an HTTP status code that is not 200" section here:
http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0
I don't know what the motivation is for this requirement. Perhaps its because a 404 could introduce confusion as to whether the endpoint actually exists.

Categories