Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error reading multipart Batch Response #1276

Open
GLuca74 opened this issue Jul 11, 2024 · 6 comments
Open

Error reading multipart Batch Response #1276

GLuca74 opened this issue Jul 11, 2024 · 6 comments
Assignees
Labels
bug Something isn't working

Comments

@GLuca74
Copy link

GLuca74 commented Jul 11, 2024

Hello, I am trying to build an OData server using Microsoft.AspnetCore.OData.
I have a problem when I try to send a Batch request with a multipart request. The problem seems happend in the response.
In this repository, https://github.com/GLuca74/TestOData, there is a solution with 3 projects :

TestODataServer - The server
TestODataClient - The client
TestODataModels - A shared Entity

the client request is processed by the server and it is able to receive the response, but when I try to read the actual response I get an error.
To read the response I use Microsoft.AspNet.WebApi.Client.
This is the code :

                using (var response = await httpClient.SendAsync(request).ConfigureAwait(false))
                {
                    if (!response.IsSuccessStatusCode)
                    {
                        HttpRequestException ex = new HttpRequestException(await response.Content.ReadAsStringAsync(), null, response.StatusCode);
                        throw ex;
                    }
                    var multipart = await response.Content.ReadAsMultipartAsync(); 
                    foreach (var c in multipart.Contents)
                    {
                        var responses = await c.ReadAsMultipartAsync();
                        foreach (var r in responses.Contents)
                        {
                            var xx1 = await r.ReadAsHttpResponseMessageAsync(); //here i get an exception
                        }

                    }
                }

The exception is :

System.ArgumentException
  HResult=0x80070057
  Messaggio=Invalid 'HttpContent' instance provided. It does not have a content type header with a value of 'application/http; msgtype=response'. Arg_ParamName_Name
  Origine=System.Net.Http.Formatting
  Analisi dello stack:
   in System.Net.Http.HttpMessageContent.ValidateHttpMessageContent(HttpContent content, Boolean isRequest, Boolean throwOnError)
   in System.Net.Http.HttpContentMessageExtensions.ReadAsHttpResponseMessageAsync(HttpContent content, Int32 bufferSize, Int32 maxHeaderSize, CancellationToken cancellationToken)
   in System.Net.Http.HttpContentMessageExtensions.ReadAsHttpResponseMessageAsync(HttpContent content, Int32 bufferSize, Int32 maxHeaderSize)
   in System.Net.Http.HttpContentMessageExtensions.ReadAsHttpResponseMessageAsync(HttpContent content, Int32 bufferSize)
   in System.Net.Http.HttpContentMessageExtensions.ReadAsHttpResponseMessageAsync(HttpContent content)
   in TestODataClient.Program.<Main>d__0.MoveNext() in D:\VM\TestOData\TestODataClient\Program.cs: riga 75

sniffing request and response with Fiddler, i have this :

POST https://localhost:7200/$batch HTTP/1.1
Host: localhost:7200
OData-Version: 4.0
OData-MaxVersion: 4.0
Accept: multipart/mixed
Accept-Charset: UTF-8
Connection: Keep-Alive
Content-Type: multipart/mixed; boundary=batch_3a22c121-57c7-49d9-9cd5-3f6605bb80c6
Content-Length: 639

--batch_3a22c121-57c7-49d9-9cd5-3f6605bb80c6
Content-Type: multipart/mixed; boundary=changeset_1f9fece3-486f-4279-a568-8fffe53f3653

--changeset_1f9fece3-486f-4279-a568-8fffe53f3653
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1

POST /Cities 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

{"CityID":"bad222ce-f863-4647-ba4c-c0929eafb971","CityName":"Madrid"}
--changeset_1f9fece3-486f-4279-a568-8fffe53f3653--

--batch_3a22c121-57c7-49d9-9cd5-3f6605bb80c6--

HTTP/1.1 200 OK
Content-Type: multipart/mixed;boundary=batchresponse_d96b63e0-77dd-41aa-b3ac-0e9d6cb38562
Date: Thu, 11 Jul 2024 14:07:49 GMT
Server: Kestrel
Transfer-Encoding: chunked
OData-Version: 4.0

1fe
--batchresponse_d96b63e0-77dd-41aa-b3ac-0e9d6cb38562
Content-Type: multipart/mixed; boundary=changesetresponse_631283f4-b5fd-48b4-8e9e-4244ca32cb53

--changesetresponse_631283f4-b5fd-48b4-8e9e-4244ca32cb53
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1

HTTP/1.1 201 Created
Location: https://localhost:7200/Cities(bad222ce-f863-4647-ba4c-c0929eafb971)
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true; charset=utf-8
OData-Version: 4.0


fe
{"@odata.context":"https://localhost:7200/$metadata#Cities/$entity","CityID":"bad222ce-f863-4647-ba4c-c0929eafb971","CityName":"Madrid"}
--changesetresponse_631283f4-b5fd-48b4-8e9e-4244ca32cb53--
--batchresponse_d96b63e0-77dd-41aa-b3ac-0e9d6cb38562--

0

It seems that ReadAsHttpResponseMessageAsync not like this

--changesetresponse_631283f4-b5fd-48b4-8e9e-4244ca32cb53
Content-Type: application/http // msgtype=response seems missing

I tryed in a custom ODataBatchHandler to found a way to add msgtype=response to the response but i wanst able to found if is possible and where

I also tryed to use Microsoft.OData.Client and it works without problems, but I can not use this library.

Can you help me found a solution?

@GLuca74 GLuca74 added the bug Something isn't working label Jul 11, 2024
@julealgon
Copy link
Contributor

Is there a particular reason why you are not using the native OData client to read the OData payloads? I'd recommend using that if you can, otherwise you are going to be reinventing the wheel a lot.

@GLuca74
Copy link
Author

GLuca74 commented Jul 11, 2024

Hello @julealgon , in the last years i wrote a no relational provider for entityframework core for a custom protocol.
In the last months i rewrote the provider separating the part that comunicate with efcore structure and the part that implements my protocol.
As result I have an assembly that is equivalent to Microsoft.EntityFrameworkCore.Relational(but for no relational) and an assembly that just implements my protocol.
Now, using my no relational assembly I am writing an OData provider for entity framework core, so my code is running inside the SaveChange implementation of Entity framework.
As general rule, I not like add reference to other assembly, for many reasons, if I fond a bug I have no control on the time to fix it, the support can ends etc.
But in this specific case, OData client need its generated models to work while I have not a fixed model to work with because it depend on the model of the developer that uses my provider and honestly I can not image (and I not want to face) wath may means make ODataClient work with a "Dynamic" model.

Another reason is that OData Client returns already instanciated objects, I not need this, it is redundant because the object creation and properties assignments are made in the efcore shaper, I will be forced to generate all objects twice.

@julealgon
Copy link
Contributor

Thanks for clarifying @GLuca74 , sounds like you have a fairly low-level, advanced case.

I know this doesn't necessarily answer your specific question, but have you tried using JSON OData batch request/responses instead of the raw multipart request approach? That format should be substantially simpler to parse as it returns a single JSON object like a normal API would.

@GLuca74
Copy link
Author

GLuca74 commented Jul 12, 2024

good morning @julealgon, yes, I saw that option and it is very confortable, but it was introduced in OData 4.01. This means that if i choice that way, I will be not able to use my provider with older version. Other OData servers may not implement this feature. I prefer try to implement the multipart rappresentation first, this gives me a wider range of use of my provider.

@gathogojr
Copy link
Contributor

Hi @GLuca74. it was introduced in OData 4.01 - I don't believe this statement statement is valid. Json batch requests and responses are supported in OData V4

@GLuca74
Copy link
Author

GLuca74 commented Jul 16, 2024

Hi @gathogojr , I read this : https://docs.oasis-open.org/odata/new-in-odata/v4.01/cn04/new-in-odata-v4.01-cn04.html#_Toc21700090

"8.11 New: JSON Batch Requests and Responses
Version 4.0 only defined a multipart format for describing batch requests and responses, which was somewhat hard to implement.

Version 4.01 introduces an alternative JSON format for batch requests and responses, so clients can now use off-the-shelf JSON libraries to compose batch requests and consume batch responses. Combined with the CSDL JSON representation [OData-CSDLJSON] this allows pure JSON communication with OData 4.01 services."

@gathogojr gathogojr self-assigned this Jul 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants