PATCH request losing body on IIS but not on localhost - asp.net-core

I have a web API, where I'm trying to support a PATCH request with a JSON Patch body to make changes to an object on the server.
I am using ASP Core with .Net 6, hosting using IIS on my web host.
This is the controller method:
public class BaseDataController<TEntity, TDetail, TNew> : ControllerBase
where TEntity : class, IIdentifiable
{
[HttpPatch("{id}")]
public virtual async Task<ActionResult<TDetail>> Patch(Guid id, [FromBody] JsonPatchDocument<TEntity> patch)
{
var item = await MainService.GetAsync(id);
if (item == null)
{
ControllerLogger.ItemNotFound();
return NotFound();
}
patch.ApplyTo(item, ModelState);
ValidationHelper.ValidatePatch(item, ModelState);
if (!ModelState.IsValid)
return BadRequest(ModelState);
await MainService.UpdateAsync(item);
return this.GuardResult(Mapper, item);
}
}
When I try to use this on my local machine, it works just fine. When I deploy to my web server, and make an identical request, I get a validation error and a 400 status code:
{"errors":{"":["A non-empty request body is required."],"patch":["The patch field is required."]}}
If I change HttpPatch to HttpPost and update the web request accordingly, it works fine.
Can anyone suggest what might be going wrong here? I'm assuming the server is baulking at the PATCH verb, but I can't work out how to make it happy. Googling is coming up with a load of WebDAV things, but the error codes don't match and ASP is clearly receiving the request (proven from the logs) when the description of the WebDAV issues suggests it wouldn't.
My working theory is that IIS is seeing the PATCH verb and doing something to the request body that ASP Core doesn't like, but I can't work out why or where to look to turn that sort of thing off.

When I try to use this on my local machine, it works just fine. When I
deploy to my web server, and make an identical request, I get a
validation error and a 400 status code: If I change HttpPatch to HttpPost and update the web request accordingly, it works fine
Well, your scenario is pretty obvious in context of IIS as you may know Http verb PATCH is not enabled as default accepted http verb on IIS Request Restrictions As you can see below:
Solution:
To resolve above incident, you outght to configure Request Restrictions on IIS and need to include ,PATCH so that it will allow http PATCH verb. On top of that, after that, please restart your application pool. You can follow below steps to implement that:
Step: 1
Select your app on IIS and then click on Handler Mappings Just as following
Step: 2
Select (Double click) ExtensionlessUrlHandler-Integrated-4.0
Step: 3
Click on Request Restrictions
Step: 4
Select VERB then include PATCH by comma seperated value as ,PATCH and click OK finally restart your application pool.
Note: For more details you can have a look on our official document here.

Related

Angular with OData batch request fails on IIS

I am using Odata with .Net Core web api. For client side i am usin angular 13. OData batch request works fine in localhost. After published on iis with 'ng-build' i am getting a cors error from odata batch request. I already allow origins. What am i missing?
I am not sure but think it was really related with cors.
I only added this code block in to "Configure" method in startup.cs and problem solved.
app.Use((context, next) => {
context.Response.Headers["Access-Control-Allow-Origin"] = "*";
return next.Invoke();
});
But before the batch, i didn't need this code block i have added. All request works fine (except batch) without the code block i have added. I am trying to understand this part.

ASP.NET Web API 2 BadRequest content

I'm not receiving the expected Response Content on the client when the resource returns BadRequest.
[HttpGet]
[Route("Test", Name = "Test")]
public async Task<IHttpActionResult> Test()
{
var result = BadRequest("test");
return result;
}
On client (see hurl.it example below) I simply receive the string Bad Request in the body:
The response on the server seems to be fine:
It was working fine at some point (returning strings or ModelState in content) and recently we noticed this problem. I can't think of any recent change on server that could cause it.
It works neither locally nor when deployed on server.
It can be reproduced in any ApiController in the project.
return Ok("test"); works as expected.
Does anyone know what can cause this behavior?
Thank you!
It is hard to tell what goes wrong.
Things you could check:
Perhaps is your error caused by invalid authentication request
Try with a new project, if that makes difference then you know it's your project and there are no errors caused by your local IIS and server settings (highly unlikely but you never know.
Check your App_Start folder, containing the BundleConfig, RouteConfig, FilterConfig,WebApiConfig`. Perhaps some custom settings did cause to give you bad request error while it might be a not found error.
Check if it's only on Get request or also on others, could be caused by different versions of assemblies.
Check if you only have the problem with 400, or does 401, 500 gives the same problem?
Check your Web.Config file, these might contain <CustomErrors> that might redirect, or throw there own errors.
After some comments, custom erros seemed to be the problem.

How to retrieve headers on AppHarbor hosted WCF-applications

Currently I have a WCF-based service deployed on AppHarbor. I'm having a issue with a GET operation defined like this:
[WebGet(UriTemplate = "feedcallback")]
Stream HandleMessageGet();
And implemented like this:
public Stream HandleMessageGet()
{
var value = WebOperationContext.Current.IncomingRequest.Headers["header.name"];
//Do stuff with header value
return ms;
}
Whenever I run this WCF application on localhost for debugging etc. it works fine; I can retrieve the header value. But whenever I deploy the project to AppHarbor, the get request doesn't function properly anymore because it can't retrieve the header from the WebOperationContext.
What could be causing this issue and how could this be solved?
In the end it seems to be an issue with the AppHarbor loadbalancer not forwarding headers with dots in them.
See: http://support.appharbor.com/discussions/problems/37218-header-not-being-forwarded

NetworkCredentials and Authorization in WebApi

I am having a few problems trying to connect to a ASP.NET webapi service (which I am running myself) from a sample console app using WebClient. The webapi is the typical sample site from MVC4:
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.OK, new string[] { "value1", "value2" });
}
The Controller is decorated with a custom Authenticate attribute:
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.Request.Headers.Authorization == null)
{
var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.Headers.Add("WWW-Authenticate", "Basic realm=\"localhost\"");
actionContext.Response = response;
return;
}
}
The client code is the usual:
var wb = WebRequest.Create("http://localhost:64921/Values");
wb.Credentials = new NetworkCredential("xxx", "xxx");
var aaa = wb.GetResponse();
Console.WriteLine(aaa);
Console.ReadLine();
Now, I know that the WebClient or WebRequest are supposed to wait for a 401 before sending credentials and that is exactly what I am trying to do here.
Needless to say with the setup above nothing works. I have gone into the IIS express config and changed the following:
<basicAuthentication enabled="true" /> (in the security section)
<add name="BasicAuthenticationModule" lockItem="false" /> (in the modules section)
The problem that I am having is that the 401 gets returned even before the server code is actualy hit. I mean that if I stick a breakpoint into the Controller or the Attribute they are not hit. The details of the error are the usual long text about error 401.2 which I reckon is something to do with IIS configs, but using IIS express and not the nice IIS I do not have a nice GUI to fix this. Can anyone help?
Thanks a lot!
In the IIS config, you have enabled Basic auth processing, so IIS returns the 401 if there are no credentials or the credentials are invalid.
If you want your code to do the basic auth processing, then you need to tell IIS to allow anonymous access.
EDIT from comments
If you ask IIS to do basic auth it will check credentials against Windows accounts. This will act before the server code runs, so the Custom Auth Filter will not be hit. In this case the headers returned will be correct and you will see the WebClient performing the double request (one anonymous, one with credentials). If the WebClient does not use a computer or domain account (with read permissions on the folder where the site is located), the request will fail.
If you want to do authentication/authorization yourself, you need to tell IIS express not to do any auth and then do it all yourself... this basically means leaving everything as it is in the config (in your case reverting the pieces of config shown in the question) and sending the correct headers, which you already do. If you debug, you will see the Authenticate filter being hit twice, the first time it will be an anonymous that will go inside the if and generate your HTTP 401 Challenge response, the second time it will have credentials in the form of a standard Basic Authorization header: Basic <BASE64_ENCODED_CREDENTIALS>

IIS 7 Serves GET request correctly to browser but throws timeout exception for API request

I am running a very simple Web application (Asp.Net MVC3) on Win 7 IIS.
I have a very simple HTTP GET API which returns hello world.
Calling:
http://localhost/helloworld
Returns:
Hello World!
This works perfectly over a browser.
But when I write an app which tries to pull this URL using a webclient, I get the following error:
{"Unable to read data from the transport connection: The connection was closed."}
My Code is as follows
WebClient web = new WebClient();
var response = web.DownloadString("http://localhost/helloworld");
My IIS Settings are as follows
What should I be looking at? I have been at this for hours and I have run out of options to try! Any help will be really appreciated!
Thanks.
I suspect it's because WebClient does not send some of the HTTP headers:
A WebClient instance does not send optional HTTP headers by default. If your request requires an optional header, you must add the header to the Headers collection. For example, to retain queries in the response, you must add a user-agent header. Also, servers may return 500 (Internal Server Error) if the user agent header is missing. http://msdn.microsoft.com/en-us/library/system.net.webclient(v=vs.80).aspx
Try using HttpWebRequest instead. http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.aspx
I finally figured out what the issue was and instead of it being an IIS specific issue - which I was leaning towards, it turned out to be an issue with the code that I wrote.
Adding details here incase someone else runs into a similar problem.
I had the following method in my code which I was using to send the response of the request as a JSON object.
private void sendJsonResult(string result) {
Response.StatusCode = 200;
Response.Headers.Add("Content-Type", "application/json; charset=utf-8");
Response.Flush();
Response.Write(result);
Response.End();
Response.Close(); // <-- This is the problem statement
}
On digging around a bit, I found out that we should not be doing a Response.Close().
A better explanation of this is here.
Once I removed that line, it started working perfectly - both in my consuming app as well as the web browser, etc.
If you will read the link above, you will clearly understand why we should not be using a Response.Close() - so I will not go into that description. Learnt a new thing today.