Search code examples
c#angular.net-coreform-data

Pass FormData Item array of object angular


How can I pass FormData to api with array of obejcts inside?

i am trying like this

enter image description here

this is the payload of request how can i get all items with other data i .net core c# Api?

public class BackgroundCheckParam
{
    public Guid EmpId { get; set; }
    public List<BackgroundChecksResults> Items { get; set; }
    public IFormFile ResultPdf { get; set; }
    public IFormFile DisclosurePdf { get; set; }
    public DateTime CompleteDate { get; set; }
}

public class BackgroundChecksResults
{
    public string Type { get; set; }
    public string Result { get; set; }
}


   public ResponseResult AddBackgroundCheck([FromForm] BackgroundCheckParam data)
    {

    }

All other is fine I am receiving everything but Items. Items are always 0.


Solution

    • It isn't working because you're mixing JSON and traditional <form> data.

      • Some background info:
        • In "normal" HTML, without scripting or AJAX, a <form> can be POST-submitted as either application/x-www-form-urlencoded or as multipart/form-data.
          • application/x-www-form-urlencoded is an almost-human-readable textual representation of every key+value pair. e.g. key1=value1&key2=value2.
          • multipart/form-data is a binary encoding. You need to use this option when posting files from an <input type="file">.
        • With JavaScript-initiated HTTP requests (using fetch or XMLHttpRequest) it is commonplace to POST data as JSON (application/json) instead of either of the 2 <form> encodings.
          • Angular's HttpClient uses fetch internally.
          • It would be nice if <form> could be used to submit JSON too, without any scripting; it doesn't, oh well.
    • In your case specifically, your Items is a List<...> being represented as a JSON array in a single form item when it should be sent as multiple form items, one for each value.

      • The solution is to represent Items in a way compatible with ASP.NET's form model-binding syntax.

        • Specifically, each discrete value has its own form name, and each list element is referenced by a 0-based incremental index.
      • In your case, the keys/names for Items will be like:

        Items[0].Type   = "Background"
        Items[0].Result = "Pass" 
        Items[1].Type   = "Foreground"
        Items[1].Result = "Fail" 
        Items[2].Type   = "Middleground"
        Items[2].Result = "Compromise" 
        
        • Note that ASP.NET (all versions) always require collection indexers to be 0-based and have no gaps between indexes, otherwise the List<> property will be null or empty and there won't be any warnings or errors (which is annoying).

    So you should have something like this:

    // * Use `URLSearchParams` for `application/x-www-form-urlencoded`.
    // * Use `FormData` for `multipart/form-data`.
    
    const resultPdfFile = inputElement1.files[0];
    const disclosurePdf = inputElement2.files[0];
    
    const formValues = new FormData();
    formValues.append( "EmpId"        , "fckgw-rhqq2-yxrkt-8tg6w-2b7q8" );
    formValues.append( "ResultPdf"    , resultPdfFile );
    formValues.append( "DisclosurePdf", disclosurePdf );
    formValues.append( "CompleteDate" , "2021-12-01" );
    
    for( let i = 0; i < items.length; i++ ) {
        
        const keyPrefix = "Items[" + i.toString() + "].";
        formValues.append( keyPrefix + "Type"  , items[i].type   ); // e.g. "Items[0].Type"
        formValues.append( keyPrefix + "Result", items[i].result );
    }
    
    await postRequest( formValues );
    

    Alternative quick-fix workaround "solution"

    In your ASP.NET code, ignore the Items member of your BackgroundCheckParam data action parameter and JSON-decode Items from Request.Form:

    public ResponseResult AddBackgroundCheck([FromForm] BackgroundCheckParam data)
    {
        IReadOnlyList<BackgroundChecksResults> items;
        {
            String formList = this.Request.Form["Items"];
    
            items = JsonConvert.DeserializeObject< List<BackgroundChecksResults> >( formList );
        }
    }