Search code examples
c#asp.netpostodata

Why is my OData POST body to model not working?


I am using OData v4 in an ASP.NET application. Every time I use Postman to post to my endpoint, I get the following error. The issue is with this line public IHttpActionResult Post([FromBody] HolidayPost data) because the function will run when I remove [FromBody] HolidayPost data. I cannot seem to get my POST body to align with the HolidayPost model. I am not sure what I am doing wrong.

Message

"No MediaTypeFormatter is available to read an object of type 'HolidayPost' from content with media type 'application/json'.",

type

System.Net.Http.UnsupportedMediaTypeException

stacktrace

" at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)\r\n at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)"

My Postman POST request

  • URL: http://localhost/PortOData4/Holidays
  • Headers: Content-Type: application/json
  • Body:
{
    "Dates": "[\"2022-01-20T00:00:00.000-06:00\",\"2022-02-20T00:00:00.000-06:00\",\"2022-03-23T00:00:00.000-05:00\"]",
    "apikey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

WebApiConfig.cs

namespace PortalogicOData
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.EnableCors();

            // Create OData entities
            ODataModelBuilder builder = new ODataConventionModelBuilder();
            config.Count().Filter().OrderBy().Expand().Select().MaxTop(null); // enable OData Model Bound Attributes
            config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));

            builder.EntitySet<Holiday>("Holidays");

            // map entities
            config.MapODataServiceRoute("ODataRoute", null, builder.GetEdmModel());
        }
    }
}

HolidaysController.cs

namespace PortalogicOData.Controllers
{
    [EnableCors("*", "*", "*")]
    public class HolidaysController : ODataController
    {
        private readonly HolidayService m_service = new HolidayService();

        [EnableQuery]
        public IList<Holiday> Get([FromUri] string apikey)
        {
            if (GenericHelper.ValidateApi(apikey))
            {
                return m_service.Getdata();  // this works fine
            }
            else
            {
                return null;
            }
        }

        [HttpPost]
        public IHttpActionResult Post([FromBody] HolidayPost data)
        {
            // this function fails to execute everytime
        }
    }
}

HolidaysService.cs

namespace PortalogicOData.Business
{
    public class HolidayService
    {

        public List<Holiday> Holidays => Getdata();

        public List<Holiday> Getdata()
        {
            // this works fine
        }

        public Holiday Update(HolidayPost data)
        {
            // my update code
        }

    }
}

HolidayPost.cs

namespace PortalogicOData.Models
{
    public class HolidayPost
    {
        public string apikey { get; set; }
        public string Dates { get; set; }

    }
}

Holiday.cs

namespace PortalogicOData.Models
{
    public class Holiday
    {
        public int HolidayID { get; set; }
        public string FunctionResult { get; set; }
        public int ReturnCode { get; set; }
        public DateTime Date { get; set; }
    }
}

Solution

  • Apparently, OData ASP.NET forces you to only allow the body in the format of route Type (e.g. Holiday not HolidayPost). I had to create a Generic class path used for posts instead. That is easier than trying to figure out how to use OData actions and functions.