A story about a bug, an inconsistency and a solution…
I recently did a demo of using PowerShell's Invoke-RestMethod to create, read, update and delete (CRUD) data to a REST service written using ASP.NET's WEBAPI project template and ODATA controllers. Everything worked pretty much as expected except for using the PATCH method to change an existing item.
My GET worked as expected:
Invoke-RestMethod 'http://localhost:41613/odata/Courses' | select -ExpandProperty value
My DELETE worked as expected:
Invoke-RestMethod 'http://localhost:41613/odata/Courses(33)' -Method DELETE
My POST (create) worked as expected:
$bodynew = @{ CourseCode='aa111'; Description='test'; Category='test'; Title='Test Course'}
invoke-restmethod 'http://localhost:41613/odata/Courses' -Method POST -Body $bodynew
My PATCH (and MERGE) failed!
$bodyupdate = @{ Title='Updated Title!'}
invoke-restmethod 'http://localhost:41613/odata/Courses(34)' -Method POST -Body $bodyupdate
At least that gave me two hints… "no body" and "The inferred media type 'application/octet-stream' is not supported for this resource." The second one was probably the easiest to fix… tell it that I'm sending JSON.
$headerJSON = @{ "content-type" = "application/json;odata=verbose"}
This got past the Invoke-RestMethod error when using PowerShell 4, but got a new error when using PowerShell 3! "Invoke-RestMethod : The 'content-type' header must be modified using the appropriate property or method." (it's a bug!) But now my ASP.NET WebApi application threw an error, "NullReferenceException". No data was received! (Remember the "no data" error?) So, maybe it was not in the expected format. When using the jQuery AJAX method you serialize your object into JSON before sending. Maybe that would work here:
$bodyupdateAsJSON = @{ Title='Updated Title!'} | ConvertTo-Json
Invoke-RestMethod 'http://localhost:41613/odata/Courses(34)' -Method PATCH -Body $bodyupdateAsJSON
-Headers $headerJSON
Bingo!
The Question then is…
Why can I pass a -Body for a create (POST) without any additional work, but when I pass a -Body with an update (PATCH or MERGE), I have to pass the data as JSON and add a header to state that I'm sending JSON?
Will POST work with those two changes?
Actually it will! Invoke-RestMethod will do a POST using default PowerShell objects for -Body as long has you don't add a -Header that specifies JSON as the format. Invoke-RestMethod will also do a POST using JSON data as long as you do supply the right -Header. It's probably a best practice to be consistent and explicitly use JSON for both.
Live and learn…
Details:
Tested with PowerShell 3.0 and 4.0. PowerShell 3.0 fails when trying to set a header for JSON with "Invoke-RestMethod : The 'content-type' header must be modified using the appropriate property or method." (It's a bug!)
The .NET application:
- Visual Studio 2015 ASP.NET Web API project with ODATA controllers
- .NET Framework 4.5.2
- Entity Framework 6.0
- SQL Server in Azure
- Test project hosted in Azure.
.