OData / WebApi

OData Web API: A server library built upon ODataLib and WebApi
https://docs.microsoft.com/odata
Other
854 stars 475 forks source link

AddLink saved in Batch leads to System.UriFormatException #2002

Open vvdev opened 4 years ago

vvdev commented 4 years ago

Processing of payload with a reference to one of Content-ID of the same batch throws System.UriFormatException

Assemblies affected

1.0.0-rc1.20191003.1 https://github.com/OData/RESTier/tree/1.0.0-rc1.20191003.1 Also tried with newest Microsoft.AspNet.OData 7.3.0 https://github.com/OData/WebApi/releases/tag/7.3.0

Reproduce steps

Add several entities to DataServicesContext, add link between two of these entities using DataServicesContext.AddLink, save chages with SaveChangesOptions.BatchWithSingleChangeset

Please check Content-ID: 6, that piece provokes the exception.

Full payload sent: ` POST http://SERVER/odata/v4/$batch HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Content-Type: multipart/mixed; boundary=batch_9350e84b-9023-4534-9819-67caf5859cd9 Accept: multipart/mixed Accept-Charset: UTF-8 User-Agent: Microsoft.OData.Client/7.6.2 Host: SERVER Content-Length: 4625 Expect: 100-continue

--batch_9350e84b-9023-4534-9819-67caf5859cd9 Content-Type: multipart/mixed; boundary=changeset_e24f630a-44bc-47a9-986e-88b2199e9d6e

--changeset_e24f630a-44bc-47a9-986e-88b2199e9d6e Content-Type: application/http Content-Transfer-Encoding: binary Content-ID: 1

POST http://SERVER/odata/v4/Entity1Data HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Content-Type: application/json;odata.metadata=minimal Accept: application/json;odata.metadata=minimal Accept-Charset: UTF-8 User-Agent: Microsoft.OData.Client/7.6.2

{"@odata.type":"#Domain.ReferenceData.Ns1.Ns1Entity1Data","CreatedByUserId":0,"CreatedOn":"2020-01-03T14:21:49.7240272Z","DataSourceUri":null,"Id":-2147483644,"Entity1Id":-2147483645,"Name":"RESTier test","LegalAddress":{"@odata.type":"#Domain.ReferenceData.PostalAddress","City":null,"Line1":null,"Line2":null,"PostCode":null,"State":null},"LegalAddressExtension":{"@odata.type":"#Domain.ReferenceData.Ns1.Ns1AddressExtension","CityCode":null,"ContinentCode":null,"CountyCode":null,"StateAbv":null,"StateCode":null}}

... ... ... ... ...

--changeset_e24f630a-44bc-47a9-986e-88b2199e9d6e Content-Type: application/http Content-Transfer-Encoding: binary Content-ID: 3

POST http://SERVER/odata/v4/Entities2 HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Content-Type: application/json;odata.metadata=minimal Accept: application/json;odata.metadata=minimal Accept-Charset: UTF-8 User-Agent: Microsoft.OData.Client/7.6.2

{"@odata.type":"#Domain.Entity1Dependent","CreatedByUserId":0,"CreatedOn":"2020-01-03T14:21:49.7240272Z","CurrentApprovalStateId":-2147483646,"EffectiveTill":null,"Id":-2147483647,"LastModifiedByUserId":null,"LastModifiedOn":null,"Entity1Id":-2147483645}

... ... ... ... ...

--changeset_e24f630a-44bc-47a9-986e-88b2199e9d6e Content-Type: application/http Content-Transfer-Encoding: binary Content-ID: 5

POST http://SERVER/odata/v4/Events HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Content-Type: application/json;odata.metadata=minimal Accept: application/json;odata.metadata=minimal Accept-Charset: UTF-8 User-Agent: Microsoft.OData.Client/7.6.2

{"@odata.type":"#Domain.Entity2ChangedEvent","CreatedByUserId":0,"CreatedOn":"0001-01-01T00:00:00Z","Id":0,"ModifiedByUserId":0,"ModifiedOn":"0001-01-01T00:00:00Z","RowVersion":null,"StatusIntVal":1} --changeset_e24f630a-44bc-47a9-986e-88b2199e9d6e Content-Type: application/http Content-Transfer-Encoding: binary Content-ID: 6

POST $5/Domain.Entity2ChangedEvent/Entities2/$ref HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Accept: application/json;odata.metadata=minimal Accept-Charset: UTF-8 Content-Type: application/json;odata.metadata=minimal User-Agent: Microsoft.OData.Client/7.6.2

{"@odata.id":"$3"} --changeset_e24f630a-44bc-47a9-986e-88b2199e9d6e-- --batch_9350e84b-9023-4534-9819-67caf5859cd9-- `

Expected result

New entities are stored, EF many to many linking table should get new record pointing to two of newly created entities

Actual result

System.UriFormatException is thrown on server side ` HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: multipart/mixed; boundary=batchresponse_8a365bb7-f465-4a7d-8017-1285abc86dca Expires: -1 Server: Microsoft-IIS/10.0 OData-Version: 4.0 X-AspNet-Version: 4.0.30319 Persistent-Auth: true X-Powered-By: ASP.NET Date: Fri, 03 Jan 2020 14:21:50 GMT Content-Length: 1530

--batchresponse_8a365bb7-f465-4a7d-8017-1285abc86dca Content-Type: multipart/mixed; boundary=changesetresponse_a01078c3-45f4-46c4-903c-74dc0331d00d

--changesetresponse_a01078c3-45f4-46c4-903c-74dc0331d00d Content-Type: application/http Content-Transfer-Encoding: binary Content-ID: 1

HTTP/1.1 500 Internal Server Error Content-Type: application/json; odata.metadata=minimal; charset=utf-8 OData-Version: 4.0

{"error":{"code":"","message":"An error has occurred.","innererror":{"message":"Invalid URI: The format of the URI could not be determined.","type":"System.UriFormatException","stacktrace":" at void Uri.CreateThis(string uri, bool dontEscape, UriKind uriKind)\r\n at async Task Microsoft.AspNet.OData.Batch.ODataBatchRequestItem.SendMessageAsync(HttpMessageInvoker invoker, HttpRequestMessage request, CancellationToken cancellationToken, Dictionary<string, string> contentIdToLocationMapping)\r\n at async Task System.Web.Http.Controllers.ApiControllerActionInvoker.InvokeActionAsyncCore(HttpActionContext actionContext, CancellationToken cancellationToken)\r\n at async Task System.Web.Http.Controllers.ActionFilterResult.ExecuteAsync(CancellationToken cancellationToken)\r\n at async Task System.Web.Http.Controllers.ExceptionFilterResult.ExecuteAsync(CancellationToken cancellationToken)"}}} --changesetresponse_a01078c3-45f4-46c4-903c-74dc0331d00d-- --batchresponse_8a365bb7-f465-4a7d-8017-1285abc86dca-- `

Additional details

Of course, the exception is throw from Microsoft.AspNet.OData.Batch.ODataBatchRequestItem.SendMessageAsync, so i probably must create that issue in Microsoft.OData project, just wanted to be sure, that RESTier is not provoking that behavior. But as i got from OData docs, that type of references/URLs/operations should be supported in AspNet.OData library.

robertmclaws commented 4 years ago

Just looking at the payload, the URL it's using is this: $5/Domain.Entity2ChangedEvent/Entities2/$ref. If this payload is straight from Fiddler, I'm not sure how the OData Batch Handler is going to parse that out, but right off the bat I can see how that would fail.

I can transfer the issue to the right project, and make sure it gets addressed on either end. In the meantime, I would save the database items in one request, and then issue a second single request to link them together. That's what I used to do in my apps when I used the DataServiceContext.

Let's see what we can come up with.

vvdev commented 4 years ago

Hello @robertmclaws. Yes, payload is copied from Fiddler, just carefully renamed entities and cut some extra unrelated changesets.

According https://docs.microsoft.com/en-us/odata/webapi/built-in-routing-conventions section "Querying, Creating and Deleting Links" and having following code line in WebApi sources https://github.com/OData/WebApi/blob/d669038523e8dbb8e1709b1044ac0d51aed40a6a/src/Microsoft.AspNet.OData/Batch/ODataBatchRequestItem.cs#L41 i assumed this type of requests should be supported in AspNet OData and, because of that, should be properly processed by RESTier, am i wrong?

Anyway, thanks for response.

Don't you have any suggestions for me, how can i (temporarily) work that around? I have several pieces in my code, where i relay on AddLink, SetLink, DeleteLink and DetachLink functionality.

robertmclaws commented 4 years ago

My suggestion is to link the items in a separate non-batch request until we can identify the problem.

vvdev commented 4 years ago

It was first thing i tried :)

Unfortunately i got following error:

{"error":{"code":"","message":"An error has occurred.","innererror":{"message":"Currently only EntitySets can be inserted into.","type":"System.NotImplementedException","stacktrace":" at async Task Microsoft.Restier.AspNet.RestierController.Post(EdmEntityObject edmEntityObject, CancellationToken cancellationToken)\r\n at async Task System.Threading.Tasks.TaskHelpersExtensions.CastToObject(Task task)\r\n at async Task System.Web.Http.Controllers.ApiControllerActionInvoker.InvokeActionAsyncCore(HttpActionContext actionContext, CancellationToken cancellationToken)\r\n at async Task System.Web.Http.Controllers.ActionFilterResult.ExecuteAsync(CancellationToken cancellationToken)\r\n at async Task System.Web.Http.Controllers.ExceptionFilterResult.ExecuteAsync(CancellationToken cancellationToken)"}}}

URL was: http://SERVER/odata/v4/Events(9638)/Domain.Entity2ChangedEvent/Entity2/$ref

SoulKeeper7 commented 4 years ago

Any update on this ?

SoulKeeper7 commented 3 years ago

@vvdev @mikepizzo Did you find a workaround for this ? I am facing a similar issue.

SoulKeeper7 commented 3 years ago

Nevermind found a workaround with AddRelatedObject