Model Binding returns null model - asp.net-core

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

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

Vue, Axios and Google Maps Place API CORS error [duplicate]

This question already has an answer here:
Google Maps API: No 'Access-Control-Allow-Origin' header is present on the requested resource
(1 answer)
Closed 2 years ago.
I've looked at the other answers, but unless I'm not getting the answer, they don't answer my question.
Environment
NodeJS 12.19.0
VueJS 2.6.10
Axios 0.15.3
Current config for Axios:
axios.defaults.headers.common['Authorization'] = store.apiKey
axios.defaults.headers.common['Content-Type'] = 'application/x-www-form-urlencoded, application/json'
axios.defaults.headers.common.crossDomain = true
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*'
axios.defaults.headers.common['Access-Control-Allow-Origin'] = 'https://maps.googleapis.com/maps/api/*'
axios.defaults.headers.common['Accept'] = 'application/json, text/plain, text/html, application/xhtml+xml, application/xml, */*'
I've tried this as well:
axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*, https://maps.googleapis.com/'
My Axios call (from method section in a mixin):
...
loadPoi (lat, lng, radius = null, type = null) {
if (!lat || !lng) {
this.$message({message: 'Unable to load Places of Interest: invalid lat or lng values',
type: 'warning'
})
return
}
let url = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=' + lat + ',' + lng + (radius ? '&radius=' + radius : '') + (type ? '&type=' + type : '') + '&key=' + GOOGLE_API_KEY
console.log('loadPoi', url)
/*
var xmlHttp = new XMLHttpRequest()
xmlHttp.open('GET', url, false) // false for synchronous request
xmlHttp.send(null)
console.log(xmlHttp.responseText)
*/
axios.get(url).then(response => {
this.places = response.results
})
.catch(e => {
this.$message({message: 'Unable to load Places of Interest [' + e + ']',
type: 'error'
})
})
},
...
As you can see, even tried raw JavaScript call, same result.
The same URL works perfectly fine in the browser.
However when I access it from Axios, I get this error:
The raw header response:
GET /maps/api/place/nearbysearch/json?location=2.0978,103.6063&radius=5000&key=[KEY] undefined
Host: maps.googleapis.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0
Accept: application/json, text/plain, text/html, application/xhtml+xml, application/xml, */*
Accept-Language: en-GB,en;q=0.7,es-MX;q=0.3
Accept-Encoding: gzip, deflate, br
Authorization: undefined
crossDomain: true
X-Requested-With: XMLHttpRequest
Access-Control-Allow-Origin: https://maps.googleapis.com/maps/api/*
Origin: http://localhost:8080
DNT: 1
Connection: keep-alive
Referer: http://localhost:8080/
Axios use for sent HTTP request, set 'Access-Control-Allow-Origin' header on a request is meaningless.
In response, the server sends back an Access-Control-Allow-Origin
header with Access-Control-Allow-Origin: *, which means that the
resource can be accessed by any origin.
enter link description here
So, to allow CORS, the server-side need to add Access-Control-Allow-Origin: * within its HTTP response.
But, it seems the response is from google API in your case, I think such service should already allow cross-origin access, could you please produce detailed error output?

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?

Interact with a JSF application which use basic authentication programmatically

I am having difficulties interacting with a website which use basic authentication to authenticate the user.
I am working on visual basic and i have already tried to use
Dim req As HttpWebRequest = HttpWebRequest.Create("http://url.to.website.com")
adding the headers directly to the web request:
req.Headers.Add("Authorization: Basic " & Convert.ToBase64String(Encoding.Default.GetBytes("user" & ":" & "password")))
or using the network credentials:
req.Credentials = New Net.NetworkCredential("user", "password")
receiving always the same response code: 401 Unauthorized
Using Firefox developer tools i can analyze and resend some web requests and only using Firefox i am able to authenticate correctly.
Firefox report these headers:
Host: url.to.website.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http.//url.to.website.com/portal/data/pub
DNT: 1
Authorization: Basic ZmFrZTpwYXNzd29yZA==
Connection: keep-alive
So i have tried to set it manaually this way:
req.Host = "url.to.website.com"
req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20100101 Firefox/11.0"
req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
req.Referer = "https://url.to.website.com/some/path/to/file.jsf"
req.ContentType = "application/x-www-form-urlencoded"
req.KeepAlive = True
req.PreAuthenticate = True
req.Method = "POST"
req.Headers.Add("Authorization: Basic " & Convert.ToBase64String(Encoding.Default.GetBytes("user" & ":" & "password")))
with no success (receiving always the same response code: 401 Unauthorized)
Another try was with a web-browser:
WebBrowser1.Navigate("url", Nothing, Nothing, "Authorization: Basic " & Convert.ToBase64String(Encoding.Default.GetBytes(AUTH_USER & ":" & AUTH_PASSWORD)))
My objective is to authenticate, then query some pages and collect responses in order to parse them and use it later in the application.
How can i solve the issue about authentication?
The website is written using JSF and i have no control over it.
Update:
My problem is about authentication, not yet about the jsf application.
While using Firefox all work fine (I can send a request to the website and it will authenticate me right) but while using the HttpWebRequest the authentication fails, even if I set the same headers, as Written before .
I have to figure out the difference between the two requests
I had to get this working for Dukes Forest Java EE Tutorial Port to Wildfly. The code was already written, but the header was case sensitive. Anyway, the code used there is as follows:
/* Client filter for basic HTTP auth */
class AuthClientRequestFilter implements ClientRequestFilter {
private final String user;
private final String password;
public AuthClientRequestFilter(String user, String password) {
this.user = user;
this.password = password;
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
try {
requestContext.getHeaders().add(
"Authorization",
"Basic " + DatatypeConverter.printBase64Binary(
(user+":"+password).getBytes("UTF-8"))
);
} catch (UnsupportedEncodingException ex) { }
}
}
The DatatypeConverter is imported from javax.xml.bind. This code was called from the following routine, which has the HTTPClient:
Client client = ClientBuilder.newClient();
client.register(new AuthClientRequestFilter("jack#example.com", "1234"));
Response resp = client.target(ENDPOINT)
.request(MediaType.APPLICATION_XML)
.post(Entity.entity(order, MediaType.APPLICATION_XML), Response.class);
int status = resp.getStatus();
if (status == 200) {
success = true;
}
logger.log(Level.INFO, "[PaymentHandler] Response status {0}", status);
client.close();
return success;
This client code posts to a RESTful service.

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.