.NET Core Patch JsonPatchDocument works locally but not on server - asp.net

I have a very simple ASP.NET Core app which works perfectlly on my local machine.
When I publish my project everything works (GET, POST, DELETE) but not partially update (PATCH). I use JsonPatchDocument<T>. This is the code:
[HttpPatch("{id}")]
public IActionResult PartiallyUpdateNote(int id, [FromBody] JsonPatchDocument<NoteForUpdateDto> patches)
{
_log.LogInformation("Partial update of #{0}. Patches: {1}", id, patches);
if (patches == null) // <------ THIS IS ALWAYS TRUE
{
_log.LogError("Invalid patch format");
return BadRequest("Invalid patch format?");
}
This is json I try to send from Postman:
PATCH http://tomasz.look24.net/api/notes/1
headers:
Content-Type=application/json
Raw: [{ "op": "replace", "path":
"/title", "value": "ne555e" }]
This is log from server (single patch hit):
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Connection id ""0HL2VB6EGP0A9"" bad request data: ""Unexpected end of request content"" (86f1a409)
Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException: Unexpected end of request content
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.MessageBody.ForContentLength.<ReadAsyncAwaited>d__4.MoveNext()
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Executing action method "notes.api.Controllers.NotesController.PartiallyUpdateNote (notes.api)" with arguments (["1", ""]) - ModelState is Invalid (ba7f4ac2)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Partial update of #1. Patches: null (30b5833a)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [ERR] Invalid patch format (b8690943)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Executing ObjectResult, writing value "Microsoft.AspNetCore.Mvc.ControllerContext". (4e968210)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Executed action "notes.api.Controllers.NotesController.PartiallyUpdateNote (notes.api)" in 119502.6086ms (afa2e885)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Request finished in 119503.2131ms 400 text/plain; charset=utf-8 (15c52c40)
2017-02-27T22:46:46.1647691+01:00 [INF] Connection id ""0HL2VB6EGP0A9"" bad request data: ""Unexpected end of request content"" (86f1a409)
Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException: Unexpected end of request content
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.MessageBody.ForContentLength.ReadAsyncImplementation(ArraySegment`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.MessageBody.Consume(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.<RequestProcessingAsync>d__2.MoveNext()
2017-02-27T22:46:46.1647691+01:00 [INF] Connection id ""0HL2VB6EGP0A9"" communication error (46f6d175)
Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException: Error -4077 ECONNRESET connection reset by peer
I got this log after 2 minutes with message:
Server Error
502 - Web server received an invalid response while acting as a
gateway or proxy server. There is a problem with the page you are
looking for, and it cannot be displayed. When the Web server (while
acting as a gateway or proxy) contacted the upstream content server,
it received an invalid response from the content server.
What can be wrong? Is there something missing in server configuration?
When I try to get data as a string like that:
public IActionResult PartiallyUpdateNote(int id, [FromBody] string patches)
Everything is fine on the server side. There must be something with PartiallyUpdateNote (I believe) which always return null.

I found the solution.
It works when I change PATCH to POST
[HttpPost("{id}")]
I wonder why?

Related

Pact provider test returning 400 response on Windows but passing on OSX

I am trying out sample code here https://github.com/DiUS/pact-workshop-dotnet-core-v3 with PactNet - 4.3.0. The only change I have made to the sample code is that i am not working with provider states in the test and instead have the required state set as part of controller only.
While it works fine on OSX when i try it on windows (Windows Server 2019 Standard) I get the following error -
PactNet.Exceptions.PactFailureException: Pact verification failed
PactNet.Exceptions.PactFailureException
Pact verification failed
at PactNet.Verifier.InteropVerifierProvider.Execute()
at PactNet.Verifier.PactVerifierSource.Verify()
at tests.ProductApiTestsWithoutBroker.EnsureProviderApiHonoursPactWithConsumer() in C:\repos\ProofOfConcepts\Pact-Product-api\ProviderTests\ProductApiTestsWithoutBroker.cs:line 47
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
Getting to pact verification
Starting verification...
Pact verification failed
Verifier Output
---------------
Verifying a pact between ApiClient and ProductService
A valid request for all products
returns a response which
has status code 200 (FAILED)
includes headers
"Content-Type" with value "application/json; charset=utf-8" (FAILED)
has a matching body (FAILED)
Failures:
1) Verifying a pact between ApiClient and ProductService - A valid request for all products
1.1) has a matching body
expected 'application/json;charset=utf-8' body but was 'text/html'
1.2) has status code 200
expected 200 but was 400
1.3) includes header 'Content-Type' with value 'application/json; charset=utf-8'
Expected header 'Content-Type' to have value 'application/json; charset=utf-8' but was 'text/html'
There were 1 pact failures
Verifier Logs
-------------
2023-02-14T20:36:14.433512Z INFO ThreadId(33) pact_verifier: Running provider verification for 'A valid request for all products'
2023-02-14T20:36:14.433752Z INFO ThreadId(33) pact_verifier::provider_client: Sending request to provider at http://localhost:9001/
2023-02-14T20:36:14.433771Z INFO ThreadId(33) pact_verifier::provider_client: Sending request HTTP Request ( method: GET, path: /api/products, query: None, headers: None, body: Missing )
2023-02-14T20:36:14.456312Z INFO ThreadId(33) pact_verifier::provider_client: Received response: HTTP Response ( status: 400, headers: Some({"cache-control": ["no-store"], "via": ["1.1 ForcepointCGCluster"], "date": ["Tue", "14 Feb 2023 20:36:32 GMT"], "content-type": ["text/html"], "connection": ["close"], "content-language": ["en"], "content-length": ["666"]}), body: Present(666 bytes, text/html) )
2023-02-14T20:36:14.456421Z INFO ThreadId(33) pact_matching: comparing to expected response: HTTP Response ( status: 200, headers: Some({"Content-Type": ["application/json; charset=utf-8"]}), body: Present(130 bytes) )
2023-02-14T20:36:14.457552Z WARN ThreadId(33) pact_matching::metrics:
Please note:
We are tracking events anonymously to gather important usage statistics like Pact version and operating system. To disable tracking, set the 'PACT_DO_NOT_TRACK' environment variable to 'true'.
2023-02-14T20:36:14.495312Z WARN ThreadId(33) rustls::conn: Sending fatal alert DecodeError
I am using .NET 7 and i tried including PactNet.Windows nuget package but it did not help. Also does not look like long Windows path issue to me here. Any pointers would be very helpful here. Thanks in advance,
Solution:
Update PactNet package to the latest version.
Check the configuration of the environment in which you are running the tests. Ensure that the Content-Type and Content-Encoding headers are set correctly in the response.
Check the code of the API to make sure it is returning the correct response headers and body.

How to invoke a SignalR Core hub method from Postman WebSocket

I have a SignalR Core 5.0 app that works in Visual Studio 2019. I will deploy the SignalR server to IIS but want to do some testing in Postman using the new WebSockets.
Taking one of my hub methods in my VS project, let's call it "SomeHubMethod" that returns some data, what is the proper syntax to invoke the hub method?
For instance, how would I translate this C# invoke for Postman WebSocket?
SomeHubMethod = The hub method
groupxyz = The name of the client originating the call to SignalR server, and so the response from the server should be sent to "groupxyz". Let's say the response is "Hello World!"
"1234" = Just some test data.
In my VS project...
private async void SendSomeHubMethod()
{
await connection.InvokeAsync("SomeHubMethod", "groupxyz", "1234");
}
Where the response would be received in my class...
connection.On<string>("TheHubResponse", (m) =>
{
_ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Debug.WriteLine(m));
// Hello World!
});
My assembled request that I found in link below for Postman WebSocket...
{"arguments":["groupxyz", "1234"],"invocationId":"0","target":"SomeHubMethod","type":1}
On Send, Postman shows Connected but "Hello World!" is not returned from my hub.
I found this post but it is not detailed on invoke.
reference example
You can but it's kinda problematic, so let's start from beginning..
When you have your defined SignalR hub endpoint (ie. wss://localhost:5005/hub/notifications) then
Make a POST request to following URL (notice the https, not the wss): https://localhost:5005/hub/notifications/negotiate?negotiateVersion=1.
In answer you will receive following information:
{
"negotiateVersion": 1,
"connectionId": "zJ1cqyAe4FRyLCGMzzC0Fw",
"connectionToken": "HYunLu0j0IHdBY4NNrkm0g",
"availableTransports": [
{
"transport": "WebSockets",
"transferFormats": [
"Text",
"Binary"
]
},
{
"transport": "ServerSentEvents",
"transferFormats": [
"Text"
]
},
{
"transport": "LongPolling",
"transferFormats": [
"Text",
"Binary"
]
}
]
}
Get the connectionToken from the step above and copy it. Now open a websocket connection with your hub like following:
wss://localhost:5005/hub/notifications?id={connectionToken} where connectionToken is the token from previous step. The url should look like: wss://localhost:5005/hub/notifications?id=HYunLu0j0IHdBY4NNrkm0g.
Now hold something.. according to the Microsoft documentation (https://github.com/dotnet/aspnetcore/blob/main/src/SignalR/docs/specs/HubProtocol.md#overview) we need to send a handshake request with following informations:
{
"protocol": "json",
"version": 1
}
It's hard to achieve by plain text because it needs to ends with a 0xE1 ASCII character, so we need to convert the handshake request with that character to base64 and send it. I did it for you and this string is:
eyJwcm90b2NvbCI6Impzb24iLCAidmVyc2lvbiI6MX0e
Now when we have all these info, let's deep dive into Postman:
Connect to the endpoint:
Just send a request with string I pasted above to this URL with content-type: Binary using Base64.
As you can see, we are receiving message {"type": 6} what means we are connected to the Hub and it's pinging us.
You can now send/receive any messages from your hub:
Now you can change the content-type to JSON and invoke your hub endpoints.
How to invoke a SignalR Core hub method from Postman WebSocket
Short answer, you can't.
Long answer, SignalR is a protocol that requires certain ceremony to start sending and receiving messages. For example, you need an ID in the query string that is generated by the server. Then you need to send the handshake request over the transport before you can start making invocations.

Unable to modify request in middleware using Scrapy

I am in the process of scraping public data regarding metheorology for a project (data science), and in order to effectively do that I need to change the proxy used on my scrapy requests in the event of a 403 response code.
For this, I have defined a download middleware to handle such situation, which is as follows
class ProxyMiddleware(object):
def process_response(self, request, response, spider):
if response.status == 403:
f = open("Proxies.txt")
proxy = random_line(f) # Just returns a random line from the file with a valid structure ("http://IP:port")
new_request = Request(url=request.url)
new_request.meta['proxy'] = proxy
spider.logger.info("[Response 403] Changed proxy to %s" % proxy)
return new_request
return response
After properly adding the class to settings.py, I expected this middleware to deal with 403 responses by generating a new request with the new proxy, hence finishing in a 200 response. The observed behaviour is that it actually gets executed (I can see the Logger info about Changed proxy), but the new request does not seem to be made. Instead, I'm getting this:
2018-12-26 23:33:19 [bot_2] INFO: [Response] Changed proxy to https://154.65.93.126:53281
2018-12-26 23:33:26 [bot_2] INFO: [Response] Changed proxy to https://176.196.84.138:51336
... indefinitely with random proxies, which makes me think that I'm still retrieving 403 errors and the proxy is not changing.
Reading the documentation, regarding process_response, it states:
(...) If it returns a Request object, the middleware chain is halted and the returned request is rescheduled to be downloaded in the future. This is the same behavior as if a request is returned from process_request().
Is it possible that "in the future" is not "right after it is returned"? How should I do to change the proxy for all requests from that moment on?
Scrapy will drop duplicate requests to the same url by default, so that's probably what's happening on your spider. To check if this is your case you can set this settings:
DUPEFILTER_DEBUG=True
LOG_LEVEL='DEBUG'
To solve this you should add dont_filter=True:
new_request = Request(url=request.url, dont_filter=True)
Try this:
class ProxyMiddleware(object):
def process_response(self, request, response, spider):
if response.status == 403:
f = open("Proxies.txt")
proxy = random_line(f)
new_request = Request(url=request.url)
new_request.meta['proxy'] = proxy
spider.logger.info("[Response 403] Changed proxy to %s" % proxy)
return new_request
else:
return response
A better approach would be to use scrapy random proxies module instead:
'DOWNLOADER_MIDDLEWARES' : {
'rotating_proxies.middlewares.RotatingProxyMiddleware': 610,
'rotating_proxies.middlewares.BanDetectionMiddleware': 620
},

How to found out url with which the request arrived at error handler?

I send following http request:
http://localhost:8081/member/createCompany/getSmallThumbnail/
On server side I hit into controller method:
#RequestMapping("/error")
public String error(Model model, HttpServletRequest request){
if(request.getRequestURI().contains("thumbnail")){
System.out.println("thumbnail accepted");
}
request.toString();
model.addAttribute("message", "page not found");
return "errorPage";
}
At this method I want to know url with which the request arrived.
If in debug I stop inside this method I see information needed for me:
But I cannot find method in request which will return this.
Please help to return url which I want.
P.S.
Actually I have not mapped controller in my spring mvc application(url is broken) for http://localhost:8081/member/createCompany/getSmallThumbnail/. This url("/error") configured in web.xml as error page.
Your request got redispatched to /error (presumably for error processing).
If this framework follows the normal Servlet error dispatching behavior, then your original request can be found in the HttpServletRequest.getAttributes() under the various javax.servlet.RequestDispatcher.ERROR_* keys.
ERROR_EXCEPTION - The exception object
ERROR_EXCEPTION_TYPE - The type of exception object
ERROR_MESSAGE - the exception message
ERROR_REQUEST_URI - the original request uri that caused the error dispatch
ERROR_SERVLET_NAME - the name of the servlet that caused the error
ERROR_STATUS_CODE - the response status code determined for this error dispatch
What you want is
String originalUri = (String) request.getAttribute(
RequestDispatcher.ERROR_REQUEST_URI)

Web API 2 - ApiController.InternalServerError() returns HTTP 400 status code

This Web API action returns an HTTP 500 (Internal Server Error) status code:
public IHttpActionResult Post()
{
return InternalServerError();
}
But this action returns an HTTP 400 (Bad Request) status code:
public IHttpActionResult Post()
{
return InternalServerError(new Exception());
}
I would expect both actions to return a 500 status code and the second action puts some of the error's details in the response body.
My first thought is that this is a bug but I wanted to get some other input. Is there any good reason why a 400 should be returned in the second action instead of a 500?
UPDATE:
The documentation on this method reads:
Creates an System.Web.Http.Results.ExceptionResult (500 Internal Server Error) with the specified exception.
I'm thinking more and more this is a bug.
Right, this was a known issue which was fixed after Web API 2 release...you can use the following workaround to fix this issue..example:
return new ResponseMessageResult(Request.CreateErrorResponse(HttpStatusCode.InternalServerError, invalidOpException));
Following was the issue that was logged before:
https://aspnetwebstack.codeplex.com/workitem/1318

Resources