Posting form with nested object not being populated

Hi @mythz,

I’m having an issue where a nested property is not being populated when posting from a regular html form. The request object has a string property that is populated correctly but the nested object, which is a bunch of bool, is just null when the request hits the service. Below is the code for the service, the markup for the form and the headers/request body. Any ideas where I’m going wrong?

Form:

<form id="random" action="/some" method="post">
<div class="form-group row">
	<label class="col-sm-2 col-form-label text-right">Property</label>
	<div class="col-sm-10">
		<input class="form-control" type="text" id="Property" name="Property">
		<span class="help-block"></span>
	</div>
</div>
<div class="form-group row">
	<div class="custom-control custom-checkbox">
		<input type="checkbox" class="custom-control-input" id="Nested_First" name="Nested.First">
		<label class="custom-control-label" for="Nested_First">First</label>
	</div>
</div>
<div class="row">
	<div class="col-lg-12">
		<div class="btn-group-md float-right" role="group">
			<button type="submit" class="btn btn-primary">Submit</button>
			<button type="reset" class="btn btn-danger">Cancel</button>
		</div>
	</div>
</div>

Service code:

public class MyService : Service
{
	public object Post(SomeRequest request) => request.ConvertTo<SomeResponse>();
}

[Route("/some")]
public class SomeRequest : IReturn<SomeResponse>
{
	public string Property { get; set; }
	public Nested Nested { get; set; }
}

public class SomeResponse
{
	public string Property { get; set; }
	public Nested Nested { get; set; }
}

public class Nested
{
	public bool First { get; set; }
	public bool Second { get; set; }
	public bool Third { get; set; }
}

Headers and Request:

General:
Request URL: https://localhost:5001/some
Request Method: POST
Status Code: 200 OK
Remote Address: [::1]:5001
Referrer Policy: no-referrer-when-downgrade

Response Headers:
	Content-Type: text/html
	Date: Sat, 07 Sep 2019 21:10:56 GMT
	Server: Kestrel
	Transfer-Encoding: chunked
	Vary: Accept
	X-Powered-By: ServiceStack/5.61 NetCore/Windows

Request Headers:
	Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
	Accept-Encoding: gzip, deflate, br
	Accept-Language: en-GB,en;q=0.9,en-US;q=0.8,af;q=0.7
	Cache-Control: max-age=0
	Connection: keep-alive
	Content-Length: 26
	Content-Type: application/x-www-form-urlencoded
	Cookie: .AspNet.Consent=yes; ss-pid=Ujst6UrXM6j0sM5lIUWJ; ss-id=SSi8gdmSq4yVSSjwP3T5
	Host: localhost:5001
	Origin: https://localhost:5001
	Referer: https://localhost:5001/
	Sec-Fetch-Mode: navigate
	Sec-Fetch-Site: same-origin
	Sec-Fetch-User: ?1
	Upgrade-Insecure-Requests: 1
	User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36

Form Data:
	Property: a
	Nested.First: on

I should have added that this also happens when I have a List as part of the Request. I have a use case where I need a List with a complex type that is posted back via a form.

Form Data’s can only send key/value pairs, they can’t populate nested properties using the deep object notation you’re attempting, you’d need to use the JSV Format to set the entire property, e.g:

Form Data:
	Property: a
	Nested: {First:false}

But I’d personally recommend sending the Request Body as JSON which is the more appropriate Content-Type to send a deep object graphs with nested properties as all HTTP clients/middleware/analyzers are going to expect QueryString/FormData to only contain Key/Value pairs.

Shot thanks @mythz. Any idea if I can still use $.ss.bindform from ssutils to send the request body as json? I’m a big fan of bindform and the validation etc that it gives.

No you will need to construct the JSON before sending as seen in this answer:

There are some ss-utils.js APIs you can use within your submit implementation:

// construct object from HTML Form:
var obj = $(this).serializeMap();

Then modify the object with your nested object graph.

You can apply validation errors to your form using the applyErrors function passing in the deserialized responseStatus from your Response DTO, e.g:

$.ajax({ 
   type: 'POST',
   url: "...",
   contentType: 'application/json',
   dataType: 'json',
   data: obj,
   success: function(response){ ... },
   error: function (jq, jqStatus, statusText) {
        try {
            var err = JSON.parse(jq.responseText);
            $("form").applyErrors(err.responseStatus);
        } catch (e) { }      
   }
});

I know that in ASPNET MVC if you define the input id and name like nested[index].Property then on the Action it will serialize that data to your list. Are you saying this is not possible with Servicestack?

No it uses JSV for complex types in QueryString/FormData properties.
https://docs.servicestack.net/serialization-deserialization#passing-complex-objects-in-the-query-string

1 Like