Deserialize Key:Value pairs to Dictionary - serialization

I am working on deserializing data passed to a Microsoft Web API in MVC4 RC into objects of the following class:
public class EditorCreateEditSubmission
{
public string action { get; set; }
public string table { get; set; }
public string id { get; set; }
public Dictionary<string, string> data { get; set; }
}
Whenever a Web API method gets data which should map to the EditorCreateEditSubmission, the "data" field is empty, like so:
(It's okay for Table and ID to be empty)
My controller method:
public EditorServerResponse Post(EditorCreateEditSubmission ajaxSubmission)
{
//...Handle data
}
The raw header:
POST http://localhost:64619/API/Species HTTP/1.1
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://localhost:64619/Manage/Species
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host: localhost:64619
Content-Length: 134
Connection: Keep-Alive
Pragma: no-cache
action=create&table=&id=&data%5Bamu%5D=1&data%5BchemicalFormula%5D=H&data%5BcommonName%5D=Hydrogen&data%5Bstatus%5D=N&data%5Bnotes%5D=
More readable view:
action create
table
id
data[amu] 1
data[chemicalFormula] H
data[commonName] Hydrogen
data[status] N
data[notes]
Do I need to manually create a class with get/set values every possible set of incoming values? It seems like deserialization of this data into a Dictionary should be straightforward, but I'm having some difficulty finding examples inthe new RC release of Microsoft's MVC4.

I don't think that the FormUrlEncodedMediaTypeFormatter does handle this.

Related

How do I deserialize to object with default formatter in net core 5 webapi when supporting multipart/form-data?

If I make a typical controller and action, with a parameter that comes from the body, the webapi will deserialize the body content into an instance of the class type using the appropriate deserializer, based on the content-type header:
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
[System.Runtime.Serialization.DataContract]
public class MyObject
{
[System.Runtime.Serialization.DataMember]
public string Text { get; set; }
}
[HttpPost()]
public void Test([FromBody] MyObject value)
{
}
}
If I make a request to it, the request looks like this if using json:
POST https://localhost:44380/Message HTTP/1.1
Host: localhost:44380
Connection: keep-alive
Content-Length: 22
accept: */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Content-Type: application/json
Referer: https://localhost:44380/swagger/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
{"Text":"123 abc 123"}
Or this if xml (my web api is set up to support json and xml):
POST https://localhost:44380/Message HTTP/1.1
Host: localhost:44380
Connection: keep-alive
Content-Length: 82
accept: */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Content-Type: application/*+xml
Referer: https://localhost:44380/swagger/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
<?xml version="1.0" encoding="UTF-8"?>
<MyObject>
<Text>string</Text>
</MyObject>
If I am changing my webapi to use multipart mime, I can't rely on the automated deserialization of the content body. As I loop through each part, how would I deserialize each sections content based on the content-type header and the configured formatters of the webapi? I could hard code a specific serializer but I'd like to use what's configured.
var boundary = HeaderUtilities.RemoveQuotes(Request.ContentType.Boundary).Value;
MultipartReader reader = new MultipartReader(boundary, HttpContext.Request.Body);
var section = await reader.ReadNextSectionAsync();
while (section != null)
{
ContentDispositionHeaderValue contentDispositionHeaderValue = section.GetContentDispositionHeader();
Stream strData = null;
if (contentDispositionHeaderValue.IsFileDisposition())
{
FileMultipartSection fileSection = section.AsFileSection();
strData = fileSection.FileStream;
fileName = fileSection.FileName;
name = fileSection.Name;
}
else if (contentDispositionHeaderValue.IsFormDisposition())
{
FormMultipartSection formSection = section.AsFormDataSection();
name = formSection.Name;
strData = section.Body;
}
//How to deserialize strData to MyObject using the webap's configured formatters?
section = await reader.ReadNextSectionAsync();
}

Correctly perform the DELETE with RestSharp

Till now I've used RestSharp to perform POST/GET passing a JSON payload as parameter/body.
Now I've to perform a delete (you can see the example form documentation just here)
DELETE https://api.xxx.it/shipment
HTTP/1.1 Accept-Encoding: gzip,deflate
Content-Type: application/x-www-form-urlencoded
X-API-KEY: APIKEY123456789
Content-Length: 10 Host: api.xxx.it
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
id=1234567
and my code below.
public Task PerformShipmentDeleteAsync(ShipmentDeleteRequest objectRequest)
{
var client = new RestClient(settingsService.Endpoint);
var request = new RestRequest("shipment", DataFormat.Json);
request.AddHeader(Constants.XApiKey, settingsService.ApiXKey);
request.AddParameter( "text/plain",$"id={objectRequest.Id}", ParameterType.RequestBody);
var res = client.Delete(request);
return Task.CompletedTask;
}
and ShipmentDeleteRequest.cs
public class ShipmentDeleteRequest
{
[JsonProperty("id")]
public int Id { get;set; }
}
The only way I've found is to format the string in this way, but It's a hack.
How do I correctly pass the body as the example without passing a string but just the C# object?

Model Binding returns null model

I have been unable to resolve an issue with Model Binding in an action.
It DOES bind, but not all of the time. Sometimes, the value is null and so of course it throws an error.
It seems to happen about 50% of the time, sometimes after the user has been logged in for a 4 minutes or longer.
This is the action:
[HttpPost]
public IActionResult Post([FromBody]Csr csr)
{
try
{
if(csr == null)
{
throw new Exception("Associate Controller recieved csr parameter when posting a new CSR");
}
//csr.ParentCsrId = this.GetCurrentCsr().CsrId;
csr.InsertedDate = DateTime.Now;
csr.UpdatedDate = DateTime.Now;
return Ok(_associateRepository.Add(csr));
}
catch (Exception ex)
{
LogException(ex, "Associate Controller", "Post([FromBody]Csr csr)");
throw ex;
}
}
The exception is thrown and logged correctly.
This is the data that was posted according to the browser:
Headers:
POST /api/associate HTTP/1.1
Host: portal.5-15globalenergy.it
Connection: keep-alive
Content-Length: 557
Accept: application/json
Origin: https://portal.5-15globalenergy.it
Authorization: Bearer xxxzy User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
Content-Type: application/json
Referer: https://portal.5-15globalenergy.it/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Body:
{"csrTypeId":1,"countryId":2,"stateCode":"SO","birthCountryId":2,"birthStateCode":"SO","city":"Sondrio","streetPart":"STAZIONE","birthCity":"Sondrio","firstName":"Test","lastName":"User","maritalStatusId":2,"citizenshipCountryId":2,"personalTaxNum":"xxxxxx","streetName":"VIA SAN GOTTARDO","streetNum":"11","zip":"53216","landPhone":"231562145","mobilePhone":"231562145","email":"test#GMAIL.IT","bankAccountKey":"123625478325621","birthDate":"18/04/1969","orderStatusId":3,"streetCode":"","parentCsrId":"2130","syncStatusId":1,"languageId":3}
If there is an error in binding, is there a way to trap this?
I finally got this resolved. Here is the takeaway: If you get a null model, yet data was passed in the Web Request, it means that ModelBinding failed.
In my case, a misformatted date string could not be converted to a date. The result is the entire model is null, not just the data field.
The way to get the Errors is through the ModelState object. In my case, no error message was returned. But an Exception Object WAS returned.
Here is the code to concatenate it into a string:
string desc = "";
foreach (var modelState in ViewData.ModelState.Values)
{
foreach (var error in modelState.Errors)
{
if(!String.IsNullOrWhiteSpace(error.ErrorMessage))
desc = desc + " " + error.ErrorMessage.ToString();
if(error.Exception != null)
{
desc = desc + " Exception: " + error.Exception.ToString();
}
}
}

How can I replace the 'Accept' http header in AS3

all
I send a http request from flash client(AS3) to a RESTFull service. The server side response json or xml data depend on the 'Accept' parameter in http header. But, I always accept xml format data even if I set the 'Accept' to 'application/json' in the client side. With wireshark I found that there are double 'Accept' parameter in the http header. Can somebody tell me why ? And/or how to get out of this.
POST /psplatform/rest/szdata/all HTTP/1.1
Host: 203.175.156.88:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-type: application/json
Accept: application/json
Content-length: 8
public function reload():void{
data = new Object();
new URLLoader(createJSONURLRequest("http://203.175.156.88:8080/psplatform/rest/szdata/all")).addEventListener(Event.COMPLETE, loaderCompleteHandler);
}
private function createJSONURLRequest(url:String):URLRequest{
var urlRequest:URLRequest = new URLRequest(url);
urlRequest.method = URLRequestMethod.POST;
urlRequest.contentType = "application/json";
//var urlVariables:URLVariables = new URLVariables("{}");
urlRequest.data = "{name:0}";
urlRequest.requestHeaders.push(new URLRequestHeader("Accept", "application/json"));
return urlRequest;
}

Return PDF documents

I've tried implementing a custom formatter for returning PDF documents from ServiceStack. Without much luck, though. I've looked at the examples and pretty much copied the VCard sample, but replaced it with my code. My formatter looks something like this:
public class PdfFormatter
{
private const string PdfFormatContentType = "application/pdf";
public static void Register(IAppHost appHost)
{
appHost.ContentTypeFilters.Register(PdfFormatContentType, SerializeToStream, (t, s) => { throw new NotImplementedException(); });
}
public static void SerializeToStream(IRequestContext requestcontext, object dto, Stream outputstream)
{
...
}
}
I've called Register from my AppHost and the Content Type Filter is added as expected. My problem is that SerializeToStream is only called when accessing the metadata for my methods. When I call the concrete method on servicestack, an HTML response is returned.
The URL called is: http://mydomain.com/api/pdf/reply/GenerateDocument
The request headers looks like this:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,da;q=0.6
Cache-Control:max-age=0
Connection:keep-alive
Host:mydomain.com
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
The response headers look like this:
Cache-Control:private
Connection:Close
Content-Length:46
Content-Type:application/pdf; charset=utf-8
Date:Thu, 16 May 2013 19:59:27 GMT
Server:ASP.NET Development Server/11.0.0.0
X-AspNet-Version:4.0.30319
X-Powered-By:ServiceStack/3,945 Win32NT/.NET
The response is actually PDF but the debugger doesn't break in SerializeToStream.
Any help would be appreciated.
After registering the Content-Type, the client still needs to tell ServiceStack that it wants the PDF content type.
You can do this on the url:
http://example.org/myservice?format=pdf
via a pre-defined route:
http://example.org/pdf/reply/GenerateDocument
Or in the HTTP Accept header, e.g:
GET /myservice
Host: example.org
Accept: application/pdf