I am using .Net framework 4.6.1 and Swashbuckle version 5.3.2 in my WebApi project. Swagger UI is not giving an option to send the input as a request body to my POST Api which uses a custom model binder.
- Model Used :
[ModelBinder(typeof(FieldValueModelBinder))]
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public string City { get; set; }
}
- API Post method used:
[HttpPost]
// POST: api/Employee
public HttpResponseMessage Post([ModelBinder(typeof(FieldValueModelBinder))]Employee emp)
{
if (!ModelState.IsValid)
return Request.CreateResponse(HttpStatusCode.BadRequest, "Please provide valid input");
else
//Add Employee logic here
return Request.CreateResponse(HttpStatusCode.OK, "Employee added sucessfully");
}
- Model Binder used :
public class FieldValueModelBinder : System.Web.Http.ModelBinding.IModelBinder
{
/// <summary>
/// Store received data in API in KeyValuePair
/// </summary>
private List<KeyValuePair<string, string>> kvps;
/// <summary>
/// Storing error while binding data in Model class
/// </summary>
private Dictionary<string, string> dictionaryErrors = new Dictionary<string, string>();
/// <summary>
/// Implementing Base method and binding received data in API to its respected property in Model class
/// </summary>
/// <param name="actionContext">Http Action Context</param>
/// <param name="bindingContext">Model Binding Context</param>
/// <returns>True if no error while binding. False if any error occurs during model binding</returns>
public bool BindModel(HttpActionContext actionContext, System.Web.Http.ModelBinding.ModelBindingContext bindingContext)
{
try
{
var bodyString = actionContext.Request.Content.ReadAsStringAsync().Result;
if (actionContext.Request.Method.Method.ToUpper().Equals("GET"))
{
var uriContext = HttpUtility.ParseQueryString(actionContext.Request.RequestUri.Query);
if (uriContext.HasKeys())
{
this.kvps = uriContext.AllKeys.ToDictionary(k => k, k => uriContext[k]).ToList<KeyValuePair<string, string>>();
}
}
else if (!string.IsNullOrEmpty(bodyString))
{
this.kvps = this.ConvertToKvps(bodyString);
}
else
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Please provide valid input data.");
return false;
}
}
catch (Exception ex)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Please provide data in a valid format.");
return false;
}
// Initiate primary object
var obj = Activator.CreateInstance(bindingContext.ModelType);
try
{
this.SetPropertyValues(obj);
}
catch (Exception ex)
{
if (this.dictionaryErrors.Any())
{
foreach (KeyValuePair<string, string> keyValuePair in this.dictionaryErrors)
{
bindingContext.ModelState.AddModelError(keyValuePair.Key, keyValuePair.Value);
}
}
else
{
bindingContext.ModelState.AddModelError("Internal Error", ex.Message);
}
this.dictionaryErrors.Clear();
return false;
}
// Assign completed Mapped object to Model
bindingContext.Model = obj;
return true;
}
I am facing below issues:
When we use ‘ModelBinder’ in our post method, Swagger UI is displaying this screen where the input parameter are posted in a query string and CustomModelBinder is invoked and tries to read request body to perform model binding and validation and gets null in this case.
Public HttpResponseMessage Post([ModelBinder(typeof(FieldValueModelBinder))]Employee emp)
When we use ‘FromBody’ in our post method, Swagger UI displays this
screen where we can send the input in a request body, but in this
case CustomModelBinder is not invoked and we are not able to perform
modelbinding and validation.
public HttpResponseMessage Post([FromBody]Employee emp)
When we try using both ‘modelbinder’ and ‘frombody’, Swagger UI takes the input as a query and we get the below response:
Tried with Postman, the API works fine and we are able to pass the input in request body and get the proper output. The custom model binding also works and populates the error message in case of invalid model state and we can then use those messages to send in the response.
What needs to be changed to invoke the custom model binder from Swagger UI while posting input data to API in request body. Please Suggest.
You can do that with an IDocumentFilter
here is the code:
private class ApplyDocumentVendorExtensions : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
{
if (swaggerDoc != null)
{
foreach (var path in swaggerDoc.paths)
{
if (path.Value.post != null && path.Value.post.parameters != null )
{
var parameters = path.Value.post.parameters;
if (parameters.Count == 3 && parameters[0].name.StartsWith("emp"))
{
path.Value.post.parameters = EmployeeBodyParam;
}
}
}
}
}
private IList<Parameter> EmployeeBodyParam
{
get
{
return new List<Parameter>
{
new Parameter {
name = "emp",
@in = "body",
required = true,
schema = new Schema {
@ref = "#/definitions/Employee"
}
}
};
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With