Hi @Yiyi Chen
Simple properties like
Id
andstring[]
are mapped correctly but I can't get the values ofSorting
andPaging
into the controller. When I check in debug they are alwaysnull
.
This issue is caused by the following:
- In the PagingParam and SortingParam class, the property is missing the setter accessor.
- The swagger UI does not generate the correct parameter formatting. From the url, we can see that the Sorting value is a json string.
the correct URL should like this:
http://localhost:5280/api/v1/user?Sorting[0].SortBy=Email&Sorting[0].SortDirection=0&Paging.Skip=0&Paging.Take=3
To solve this issue, you can modify the model as below: Add setter and Constructor.
public class PagingParam
{
public int Skip { get; set; }
public int Take { get; set; }
public PagingParam() { }
public PagingParam(int skip, int take) => (Skip, Take) = (skip, take);
}
public class SortingParam
{
public string? SortBy { get; set; }
public SortDirection? SortDirection { get; set; }
public SortingParam() { }
public SortingParam(string sortBy, SortDirection sortDirection) =>
(SortBy, SortDirection) = (sortBy, sortDirection);
}
Then, for the Swagger UI parameter incorrect issue, you can contact the Swagger UI to verify if there is a workaround to fix the incorrect parameter formatting issue for the complex object.
Or, you can check the API using PostMan with the correct URL (like this: http://localhost:5280/api/v1/user?Sorting[0].SortBy="Email"&Sorting[0].SortDirection=0&Paging.Skip=0&Paging.Take=3
), instead of using Swagger.
Finally, if you still want to Swagger with the incorrect parameter (json string), you can create a custom model binder to handle the data: get the json string first, then deserialize it. Code Like this:
[ModelBinder(BinderType = typeof(UserParamModelBinder))]
public class UserParam
{
public Guid? Id { get; set; }
public string? Email { get; set; }
public List<string>? Select { get; set; }
public List<string>? Include { get; set; }
public List<SortingParam>? Sorting { get; set; }
public PagingParam? Paging { get; set; }
}
public class PagingParam
{
public int Skip { get; set; }
public int Take { get; set; }
public PagingParam() { }
public PagingParam(int skip, int take) => (Skip, Take) = (skip, take);
}
public class SortingParam
{
public string? SortBy { get; set; }
public SortDirection? SortDirection { get; set; }
public SortingParam() { }
public SortingParam(string sortBy, SortDirection sortDirection) =>
(SortBy, SortDirection) = (sortBy, sortDirection);
}
public enum SortDirection
{
ASC,
DESC
}
public class UserParamModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
//get value from bindingContext
var valueproviders = bindingContext.ValueProvider;
var result = new UserParam();
if (valueproviders.GetValue("Id").Length > 0)
result.Id = new Guid(valueproviders.GetValue("Id").FirstValue);
if (valueproviders.GetValue("Email").Length > 0)
result.Email = valueproviders.GetValue("Email").FirstValue;
if (valueproviders.GetValue("Select").Length > 0)
result.Select = valueproviders.GetValue("Select").ToList();
if (valueproviders.GetValue("Include").Length > 0)
result.Include = valueproviders.GetValue("Include").ToList();
if (valueproviders.GetValue("Sorting").Length>0)
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var sortlist = new List<SortingParam>();
foreach (var item in valueproviders.GetValue("Sorting").ToList())
{
var newsort = JsonSerializer.Deserialize<SortingParam>(item.Replace("\n", ""), options);
sortlist.Add(newsort);
}
result.Sorting = sortlist;
}
var newpaging = new PagingParam();
if (valueproviders.GetValue("Paging.Skip").Length>0)
newpaging.Skip = Convert.ToInt32(valueproviders.GetValue("Paging.Skip").FirstValue);
if (valueproviders.GetValue("Paging.Take").Length>0)
newpaging.Take = Convert.ToInt32(valueproviders.GetValue("Paging.Take").FirstValue);
result.Paging = newpaging;
bindingContext.Result = ModelBindingResult.Success(result);
return Task.CompletedTask;
}
}
public class UserParaBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(UserParam))
{
return new BinderTypeModelBinder(typeof(UserParamModelBinder));
}
return null;
}
}
Then, register the binder provider in the Program.cs:
builder.Services.AddControllers(options =>
{
options.ModelBinderProviders.Insert(0, new UserParaBinderProvider());
});
The result as below:
If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment". Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.
Best regards,
Dillion