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

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();
}

Related

.Net Core blocked by CORS policy error only when uploading a file

Is there something different that needs to be done when uploading a file? Every other call (Axios/Vue put) works fine except where a file is getting uploaded. It's going from example.com to api.example.com. Everything works fine locally hitting different ports.
ConfigureServices:
services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowedToAllowWildcardSubdomains();
//.WithOrigins("http://*.example.com"); //tried adding, nothing works when it's here
}));
Configure:
app.UseCors("CorsPolicy");
And the controller:
[ApiController]
[EnableCors("CorsPolicy")]
public class MyController : ControllerBase {
Public Model Put([FromForm] ICollection<IFormFile> files, [FromForm] string jsonString)
The browser output:
Access to XMLHttpRequest at 'http://api.example.com/api/YourKnowledge' from origin 'http://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Request URL: http://api.example.com/api/YourKnowledge
Referrer Policy: strict-origin-when-cross-origin
Date: Sat, 03 Oct 2020 19:58:42 GMT
Server: Microsoft-IIS/10.0
Transfer-Encoding: chunked
X-Powered-By: ASP.NET
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 10219
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryvIG7idij6Og7BH8r
Host: api.example.com
Origin: http://example.com
Referer: http://example.com/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
files: (binary)
jsonString:{}
Last note, I did find this Asp.Net Core API CORS policy error only in file upload, but I won't have just images uploaded. Did try setting it to test, but it didn't make a difference.
I think you couldn't add two parameter with [FromForm]
(I know that its the rule at least in [FromBody]).
please let me know that works

REST API response with 400 Bad request when Content-length header is givin in POST

I have a Website and a python flask RESTFUL API. When testing the API with Postman it works just fine, but the same request using XMLHttprequest in js gives me a 400 BAD Request code. I looked at the headers send and replicated the XMLHttprequest headers in Postman, to get a 400 BAD REQUEST Code. If i remove the Content-Length header from the request, it works fine. My Postman code (HTML, without content-length):
POST /user/181453766040485888 HTTP/1.1
Host: 127.0.0.1:5000
Content-Type: multipart/form-data; boundary=---
-WebKitFormBoundary7MA4YWxkTrZu0gW
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keeep-alive
Host: 127.0.0.1:5000
Origin: http://localhost:52014
Referer: http://localhost:52014/settings.php
User-Agent: Mozilla/5.0 (Windows NT 10.0;
Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 OPR/57.0.3098.106 (Edition Campaign 76)
cache-control: no-cache
Postman-Token: 5dd34b0b-965c-4d31-83b9-2364b03c0aa2
Content-Disposition: form-data; name="status"
0
Content-Disposition: form-data; name="username"
0
Content-Disposition: form-data; name="token"
0
Content-Disposition: form-data; name="display_names"
1
Content-Disposition: form-data; name="messages"
1
Content-Disposition: form-data; name="roles"
1
Content-Disposition: form-data; name="votes"
1
------WebKitFormBoundary7MA4YWxkTrZu0gW--
My js function (for testing):
function save() {
var formData = new FormData();
formData.append("status", 0);
formData.append("username", 0);
formData.append("token", 0);
formData.append("display_names", 0);
formData.append("messages", 0);
formData.append("roles", 0);
formData.append("votes", 0);
const Http = new XMLHttpRequest();
const url='http://127.0.0.1:5000/user/181453766040485888';
Http.open("POST", url);
Http.onreadystatechange=(e)=>{
console.log(Http.responseText)
}
Http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
Http.send(formData);
}
my API function that handles the request (abstract):
def post(self, id):
if database.execute(database.SAVE_USER_SETTINGS.format(id, request.form["status"], request.form["username"],
request.form["token"], request.form["display_names"],
request.form["messages"], request.form["roles"],
request.form["votes"])):
return (id, request.form["status"], request.form["username"],
request.form["token"], request.form["display_names"],
request.form["messages"], request.form["roles"],
request.form["votes"]), 201
return {}, 500

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;
}

Deserialize Key:Value pairs to Dictionary

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.

GET webrequest parameters

I am sending a GET request to a site and would like to know what would be the proper way to do this based on the following parameters.
Host: www.somesite
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.6) Gecko/20100625
Firefox/3.6.6 GTB7.1 ( .NET CLR 3.5.30729; .NET4.0E)
Accept: text/javascript, text/html, application/xml, text/xml, /
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
X-Requested-With: XMLHttpRequest
X-Prototype-Version: 1.6.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://www.somewebsite.com/search/
Content-Length: 19
Cookie: __some cookie
Pragma: no-cache
Cache-Control: no-cache
I did use firebug to get this and now am trying to create my own request header as follows:
webRequest = TryCast(System.Net.WebRequest.Create(url), HttpWebRequest)
Thread.Sleep(New TimeSpan(0, 0, 10))
'webRequest.Credentials = credentials
webRequest.Headers.Add("Cookie", cookielogin)
webRequest.Method = method__1.ToString()
webRequest.ServicePoint.Expect100Continue = True
webRequest.ContentType = "application/x-www-form-urlencoded"
webRequest.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 GTB7.1 ( .NET CLR 3.5.30729; .NET4.0E)"
webRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
webRequest.KeepAlive = True
my url that I have shows in firebug post header the following parameters:
ajax 1
page 2
q item
Now I have included this in my get request since I need to retrieve multiple pages but I only get page 1 back. Am I missing something
Get firebug, you can view the request header and set your custom request header to/from server
I was able to resolve this; thanks for the response.
It involved placing parameters within the body of the request.