KevinDockx / JsonPatch

JSON Patch (JsonPatchDocument) RFC 6902 implementation for .NET
MIT License
173 stars 29 forks source link

Operations.Count() is always 0 #105

Open chriscfox opened 3 years ago

chriscfox commented 3 years ago

Please can you help:

I have implemented my controller action as [HttpPatch] [Authorize] [Route("Projects/{projectID}/Insights/{id}")] public ActionResult InsightPatch (Guid projectID, Guid id, JsonPatchDocument patchData) { ... some stuff... }

I then call into this using JQuery, like so: var patch = [{ "op": "replace", "path": "/title", "value": "some string value" }]; $.ajax({ url: "/Projects/" + $(this).parents("form").find("#ProjectID").val() + "/Insights/" + $(this).parents("form").find("#ID").val(), data: JSON.stringify( patch ), type: 'PATCH', contentType: 'application/json', success: () => { console.log("PATCH Submitted"); } });

However, Operations.Count() is always 0 in the controller action. I am targetting .Net Framework 4.6.1

Can anyone please help?

KevinDockx commented 3 years ago

Could you show the exact request that's sent over the wire (headers + body) (you can find that via F12)?

chriscfox commented 3 years ago

Hi Kevin,

Here is what is posted:

Request URL: https://localhost:44348/Projects/7d824fff-4b51-4187-a8e9-229ce2195485/Insights/149f6ee2-9164-4666-9390-776439182b2d Request Method: PATCH Status Code: 404 Remote Address: [::1]:44348 Referrer Policy: strict-origin-when-cross-origin cache-control: private content-length: 4057 content-type: text/html; charset=utf-8 date: Wed, 22 Sep 2021 09:54:07 GMT server: Microsoft-IIS/10.0 x-aspnet-version: 4.0.30319 x-powered-by: ASP.NET x-sourcefiles: =?UTF-8?B?QzpcVXNlcnNcY2hyaXNcR2l0SHViXFNMQVxTTkFcUHJvamVjdHNcdW5kZWZpbmVkXEluc2lnaHRzXHVuZGVmaW5lZA==?= :authority: localhost:44348 :method: PATCH :path: /Projects/7d824fff-4b51-4187-a8e9-229ce2195485/Insights/149f6ee2-9164-4666-9390-776439182b2d :scheme: https accept: / accept-encoding: gzip, deflate, br accept-language: en-GB,en;q=0.9,en-US;q=0.8 content-length: 62 content-type: application/json cookie: _ga=GA1.1.361070080.1575743670; ASP.NET_SessionId=k3ede5ltfgi1fqwhyu0wtfit; utmc=111872281; GoogleCalendarAPI=True; WebSource=SWOT; hubspotutk=e408995dabf0812356e4e6c81cfbba1a; hssrc=1; utma=111872281.361070080.1575743670.1596798149.1596812272.25; ProjectNam=A; gads=ID=33c48b3696c648da-226869934da60037:T=1603116529:RT=1603116529:S=ALNI_MYwJNAdtcx2ca7wxOr8srsuSh3ZZQ; BMCNotFinE=A; BullBMCEmp=A; CustSegmen=A; LHSRHS=A; MBAActionD=A; NBAAction=A; NBAActionD=A; NBASmall=A; NBATable=A; NBAXClose=A; OverdueRed=A; ReportLin2=A; ActOverdue=A; NBAXCHelp=A; NBABMCDone=A; SWOT5to9=A; InsighFile=A; SCModal=A; SCMore=A; MDevice=A; StratBrd=A; AfterBMC=A; NBAInitiat=A; PreloadNBA=A; PreloadNB2=A; tinySlide=A; tinyGrey=A; GDPR=GDPR; 5Arrows=A; SCAdders=A; NBABlock=B; NBABorder=A; LiveUpd=A; InsighHelp=A; actionStri=A; PadBoard=A; NBATitle=A; SWONotFinE=A; PESNotFinE=A; BlogServe=A; PicDrop=A; SHContact=B; DropDowns=A; InsightSel=A; LeftMenu=B; RestrucMen=A; Expiry=A; LeftMobM=A; ActiveWhi=A; BlogServ2=A; SignUp=A; MatDiv=A; MatDiv2=A; SimpleChoo=A; MissionNBA=A; NBALinkT=A; ValueHelp=A; Articulat=A; NBATitle2=A; SCEmpty=A; PESTELHol2=A; PESTELHol3=A; PESTELHol4=A; PESTELXtr=A; PeopleCard=A; SHContact2=A; LogIn=A; NBAListCom=A; NBAListCo2=A; SignIn=A; MenuHead=A; DueDate=A; HeaderSha2=A; HeaderShad=A; BusModel=A; AMResults=A; SelectSpc=A; FunnelSpc=A; Lighter=A; SmRoundCnr=A; SurveyCookie=79a254b3-c9d3-4da0-bd74-bcd45e699181; LogonForm=A; RegisterFo=A; _gcl_au=1.1.710926079.1623241990; McKinsHold=A; SpeechBub3=A; LightBub=A; SWOTHold2=A; PESTELHol5=A; PESTELHold=A; PESTELLear=A; GovDetail2=A; GovDetail=A; Governanc2=A; Governanc3=A; Governance=A; EvidImpl=B; 5ForcesHold=A; ChessImg=A; AltHeroImg=A; HeroChess=A; ImgDown=A; Correlat=A; SWOTHold=A; SegmentDes=B; COVID=B; CycleSmall=A; hstc=181257784.e408995dabf0812356e4e6c81cfbba1a.1592976719529.1627544634037.1627549575178.29; RequestVerificationToken=4SWPLFeSbxkSwWTmE47Ya7vxBRKIQnj0OErJA2Hnn_N4fGkCS9ycmaLgy8uTyM4wjqpW0RM8KbqW4USJc0uqiqFR8Y81; ShowAds=A; ShowAds2=A; PersDevPla=A; InSeries=A; LogonForm2=A; NewBackBut=A; LoginLink=B; LoginLink2=A; NoGreenArr=A; IsMe=True; AltStart=A; _gid=GA1.1.237265193.1631984654; .ASPXAUTH=6E8132A5A81670481FF8C16922339080E3F0B820FD693682C71DC3F47DD1D7755075F9122018C908148CC9797E3ACE6483F32C4915623B79620EB935D02B5E63A36FB10624D92F626557138D8E45854198B9FB1D; _gat_UA-7704942-7=1; _ga_0CQ0QXHBML=GS1.1.1632304433.128.0.1632304434.0; uvts=f8a8bc70-573f-4185-4419-b9d0088c8f44 origin: https://localhost:44348 referer: https://localhost:44348/Projects/7d824fff-4b51-4187-a8e9-229ce2195485 sec-ch-ua: "Google Chrome";v="93", " Not;A Brand";v="99", "Chromium";v="93" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" sec-fetch-dest: empty sec-fetch-mode: cors sec-fetch-site: same-origin user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36 x-requested-with: XMLHttpRequest [{op: "replace", path: "/title", value: "some string value"}] 0: {op: "replace", path: "/title", value: "some string value"}

I have found an alternative way of doing what I need, but still, it would be good to figure out how to do it the 'right' way!

Many thanks, Chris

KevinDockx commented 2 years ago

Hi Chris,

it looks like the keys in the request body aren't surrounded by quotes.

[{op: "replace", path: "/title", value: "some string value"}]

=> should be

[{"op": "replace", "path": "/title", "value": "some string value"}]

Could you give that a try?

Hope this helps! :)

chriscfox commented 2 years ago

I tried it both ways and it didn't help. I am pretty sure the problem is with the [FromBody] decoration on the controller action. I couldn't get that to work without trying mix ASP.Net and ASP.Net Core in the same controller. So maybe that does not work? Hence I left it out in the example I posted.

Unfortunately, I have stripped it all out and found another solution to my problem, so my ability to test various things is now limited.

I'm happy if you want to close this issue, or leave it open for the next person who comes along.

Thanks for your help.

Nettsentrisk commented 2 years ago

I'm also getting this problem, in a project running .NET Framework 4.8.

I've tried forming the JSON patch request with the path name both lowercase and aligned with the JSON model of the class that the JsonPatchDocument is being mapped against, but I get an empty Operations property no matter what I try.

I've tried with the FromBody annotation and without, and tried changing the content-type of the request, none of which makes a difference, still get empty Operations property.

Is there something very mundane I am missing here? Would be nice with an example project for .NET Framework that shows how it's supposed to work.

Nettsentrisk commented 2 years ago

I see that there's no MediaFormatter that handles the JSON array that you can send from the client, which is handled in this other similar nuget package here: https://github.com/myquay/JsonPatch/blob/master/src/JsonPatch/Formatting/JsonPatchFormatter.cs

Is this handled automatically when using .NET Core but not in .NET Framework?

Nettsentrisk commented 2 years ago

AFAIK this seems to be happening because the custom JsonConverter defined for the JsonPatchDocument class is not being called, so it's just returning the default constructor for JsonPatchDocument with an empty Operations list.

Is there anyone who knows why the JsonConverter would not be called? Conflict with something else that is configured?