user3129594
user3129594

Reputation: 391

Web API PUT missing fields set to null

I have a controller with a PUT method which accepts a set of parameters via JSON and then updates the database. However default behavior for PUT seems to be that if a parameter is missing it gets reverted to null.

For example, code:

            // PUT: api/Business/5
        [ResponseType(typeof(void))]
        public async Task<IHttpActionResult> Putuser_business(int id, DTO_business_details dto_us_bus)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

//new code
                var dto_bus_details = await db.user_business.Select(us_bus =>
                new DTO_business_details()
                {
                    BusinessID = id,
                    BusinessName = us_bus.bus_name,
                    Address = us_bus.bus_address,
                    PhoneNumber = us_bus.bus_phone
                }).SingleOrDefaultAsync(us_bus=> us_bus.BusinessID == id);

            dto_us_bus.BusinessID = dto_bus_details.BusinessID;
            if (dto_us_bus.BusinessName == null) { dto_us_bus.BusinessName = dto_bus_details.BusinessName ; }
            if (dto_us_bus.Address== null) { dto_us_bus.Address= dto_bus_details.Address; }
            if (dto_us_bus.PhoneNumber == null) { dto_us_bus.PhoneNumber = dto_bus_details.PhoneNumber; }

            var user_business = new user_business()
            {
                bus_id = id,
                bus_name = dto_us_bus.BusinessName,
                bus_address = dto_us_bus.Address,
                bus_phone = dto_us_bus.PhoneNumber,

            };

            db.Entry(user_business).State = EntityState.Modified;

            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!user_businessExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return StatusCode(HttpStatusCode.NoContent);

The JSON I send works fine, however if I send BusinessName and Address (i.e. if I leave out PhoneNumber), it will change PhoneNumber to null rather than leaving PhoneNumber as it is in the DB.

I figured it's because I am setting the values to the DTO values (so if they're missing it would be null) but even if I don't use a DTO, the behaviour is the same. Am I missing something or is this just the way PUT works?

Thanks

EDIT

I thought about doing a LINQ select against the DB and then doing a check on each variable & if it's null then setting it to the data it got from the select, however this won't work as if the value actually comes through as null from the user it will leave the DB value in tact. This will work if there is a way I can determine if it's missing from the JSON call rather than if it's null, but it seems pretty inefficient.

Upvotes: 1

Views: 2356

Answers (1)

Ant P
Ant P

Reputation: 25231

This has nothing to do with PUT. If you don't post a phone number, there won't be one in the request and therefore there won't be one in the bound parameter, regardless of the verb. The MVC framework doesn't know what is in your database or what you want it to do.

It is up to you to first load the existing record and then assign only the values that you want to change.

A good start would be using a proper view model for your action method parameters that contains only the properties that you need for that action method (this will also help to prevent model injection - people including values that you don't want them to include).

If you can't know what fields are going to be set at compile time, you will either have to treat null values as unchanged or changed to null. It can't really mean both. If you want the user to be able to set values to null, you'll have to do something like:

  • Ensure that a non-null value is always sent for "unchanged" values.
  • Accept some special value to indicate "removal" of a property value.
  • Bind to some weakly typed object like a dictionary where you can check for a propert's presence.
  • Use different action methods for setting the different properties.

If you are controlling the requests, the first option would be the simplest. Just make sure that the original value is sent in the request if it isn't changed.

Upvotes: 1

Related Questions