Angular 6 headers with post request does't work - c#

I'm using Angular 6 for my front-end and a Web API based on Core2 for my server. I wrote this code and this worked when I used ASP.NET client side, but with Angular I have some troubles.
services.AddMvc():
services.AddCors(o => o.AddPolicy("FreePolicy", builder =>
{
builder.WithHeaders(<redacted>)
//builder.AllowAnyHeader()
.AllowCredentials()
.AllowAnyMethod()
.AllowAnyOrigin();
}));
If I comment builder.WithHeaders and uncomment builder.AllowAnyHeader the errors go away, but otherwise Angular crashes.
My headers:
This looks like the place with my error because "MaybeUnknown". You can see brackets around the value, so this looks like an array. Can I fix it and will it solve my problem?
I'm getting errors after my post request on a server if I don't uncomment builder.AllowAnyHeader.
UPDATE
My headers on server side
MY headers on client
UPDATE 2
Configure method
UPDATE 3

It's clear from the latest screenshot you've posted, which shows the HTTP request that's being made through Chrome, that you have a missing header in your WithHeaders call. If you look at Access-Control-Request-Headers in the request, you'll see it contains four headers:
apiss
client-id
zump-api-version
content-type
However, your WithHeaders call does not include Content-Type, so you'll need to add that:
builder.WithHeaders("client-id", "zump-api-version", "apiss", "content-type")
...
Note: This is all case-insensitive, so you can case it in whichever way you'd prefer.
There's more information about this in the MDN docs: Access-Control-Allow-Headers, which includes the following explanation:
Note that certain headers are always allowed: Accept, Accept-Language, Content-Language, Content-Type (but only with a MIME type of its parsed value (ignoring parameters) of either application/x-www-form-urlencoded, multipart/form-data, or text/plain). These are called the simple headers, and you don't need to specify them explicitly.
This explains why you don't have to specify Accept (it's a "simple header"). You do have to specify Content-Type in your example because it is neither of the three MIME types referenced in the statement above.

Related

Why is postman sending form data in an HTTP GET?

I received a Postman json collection from an API vendor that works perfectly, but has something mystifying to me: The request is in a GET format, yet there is an x-www-form-urlencoded body.
URL: https://login.microsoftonline.com/d1e<secret>9563/oauth2/token
And when I look at the postman-generated c# code, the mystery continues:
var client = new RestClient("https://login.microsoftonline.com/d1e...d3/oauth2/token");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("grant_type", "client_credentials");
request.AddParameter("client_id", "c06bb...79");
request.AddParameter("client_secret", "7~u...D");
request.AddParameter("resource", "https://vault.azure.net");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
Note the AddParameter constructions for a GET call. To me, this must be a slight-of-hand for merely adding those values to the querystring. But when I look at the postman console I see:
In the postman console I would have expected to see those params appended to the url as a querystring, and then everything would have made sense. But you can see that it's a bonafide Request Body.
When I make GET calls in my c# code I like to use the simple yet solid WebClient object to call the DownloadString() method. But this method is only for GETs and there's no way to send a form-post style body, understandably.
Is postman truly sending a GET with all those values being appended to the url as a querystring? And should I do the same in my DownloadString() call? Or is there something else going on here? Should I instead, in my c#, be calling the UploadString() method and sending a form post BODY as a GET??
Http protocol supports adding a body to a request, but the WebClient class you use doesn't. Presumably because it isn't considered the norm.
I'm sure there's good reasons for Microsoft using it in the OAuth flow though. Those guys normally do things right!
HTTP GET with request body
API is just an abstraction , you can send what ever you want to the API . It depends on the implementation , how the server handles these data.
Some services considers only what it requires and ignores other information
some services considers the entire requests and validates that it has only the allowed data. what should be allowed depends on the service
Postman is just a client that sends data to server , its upto you to decide what all information it should send . If you dont need any body then keep it as none. if you need some thing then add it.

Unexpected behavior with CORS on .NET Core

I have added the following to my service configuration.
services.AddCors(options
=> options.AddDefaultPolicy(builder
=> builder
//.AllowAnyHeader()
.WithHeaders("header-that-nobody-knows")
.WithOrigins("http://localhost:44304")));
My expectation was that the calls would bounce (as I don't add header-that-nobody-knows to my headers). However, the request is carried out just as if AllowAnyHeader() was set.
Manipulating the port, domain or protocol in WithOrigins() produces the expected result, so the config seems to be wired up properly. I suspect it's a special case somehow because I'm getting unexpected behavior with WithMetod() when it comes to GET and POST (while other methods are blocked/allowed depending on the paramers passed).
Checking MSDN gave nothing I recon as explanation.
I doubt that it matters but for completeness sake, here's the Angular code invoking the call.
let url = "https://localhost:44301/security/corstest?input=blobb";
this.http.get<any>(url).subscribe(
next => console.log("next", next),
err => console.warn("err", err));
The action method looks as below.
[HttpGet("corstest")]
public IActionResult CorsTest([FromQuery] string input)
{
return Ok(new { data = input + " indeed..." });
}
When you try to send a request to a cross-origin URL with a "non-standard" header,
the browser will perform a preflight OPTIONS request with the Access-Control-Request-Headers header that contains the non-standard headers.
OPTIONS /corstest
Access-Control-Request-Method: GET
Access-Control-Request-Headers: header-that-nobody-knows
Origin: https://my.api
ASP.NET Core inspects this value and checks if the CORS policy has AllowAnyHeader or if it explicitly allows it with .WithHeaders, if not it will issue a non-200 response and the browser will refuse to send the actual request.
So, not adding header-that-nobody-knows to request headers doesn't mean ASP.NET Core will refuse to serve the request, it means if you set header-that-nobody-knows header in a cross-origin request, it will allow it instead of issuing a non-200 response (assuming you allowed it with WithHeaders or AllowAllHeaders)
So in a nutshell:
You have to allow some/all origins + some/all headers at minimum for a CORS policy to take effect.
Browser expects both Access-Control-Allow-Headers and Access-Control-Allow-Origin in the preflight request to match the main request.
You can only send a subset (which includes 0) of the allowed headers.
References
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests

How to clean up existing response in webapi?

There is a authentication library that I have to use that helpfully does things like
Response.Redirect(url, false);
inside of it's method calls. I can't change this libraries code and it's fine for MVC style apps but in angular SPA -> WebApi apps this is just awful.
I really need a 401 otherwise I get into trouble with CORS when my angular scripts, using $http, try to call out to the auth server on another domain in response to the 302, that's if it even could as the Response.Redirect also sends down the object moved html and the angle brackets cause an error to be thrown.
Since I have to make the call to the auth library first the Response.Redirect is already in the response pipeline and so I need to clean it up to remove the body content and convert the 302 into a 401. I thought I could just:
return new HttpWebResponse(StatusCode.UnAuthorized){
Content = new StringContent("data");
}
but this just gets appended to the response and doesn't replace it plus I also need the Location: header which I can't seem to access via WebApi methods.
So instead I've had to do this in my ApiController:
var ctxw = this.Request.Properties["MS_HtpContext"] as HttpContextWrapper;
var ctx = ctxw.ApplicationInstance.Context;
var url = ctx.Response.RedirectLocation;
ctx.Response.ClearContent();
return new HttpWebResponse(StatusCode.UnAuthorized){
Content = new StringContent(url);
}
But this seems terrible and counter to webapi "feel". Plus I'm tied to the controller in doing this. I can't get the wrapper in a MessageHandler for example.
What I'd like to do is monitor the response for a given route in a message handler or in an AuthorizationFilterAttribute, if its a 302, I want to read it's headers, take what I want, wipe it and replace it with my own "fresh" response as a 401. How can I do this?
You might want to write your own ActionFilter and override its OnActionExecuted method where you can access HttpActionExecutedContext. From there, you can check response code, for example, and overwrite response with whatever you want.
Ref: https://msdn.microsoft.com/en-us/library/system.web.http.filters.actionfilterattribute.onactionexecuted%28v=vs.118%29.aspx#M:System.Web.Http.Filters.ActionFilterAttribute.OnActionExecuted%28System.Web.Http.Filters.HttpActionExecutedContext%29

.NET 4.6 HttpResponse.PushPromise methods to manage http/2 PUSH_PROMISE header

I am a bit confused about PUSH PROMISE http/2 header handling in .NET4.6.
When I look HttpResponse.PushPromise there are two overloads:
One that accepts path to resource public void PushPromise(string path) - am assuming resource is then read and binary sent across to client.
Second public void PushPromise(string path, string method, NameValueCollection headers) that accepts sting method and NameValueCollection headers which I am failing to understand.
Why would I want to pass method (assuming HttpMethod like GET, POST, etc) and collection of headers inside PUSH PROMISE header?
From reading the HTTP/2 spec (Section 8.2), here is what I gather:
Passing the method
PUSH_PROMISE frames are required to be cacheable and safe. You have the option of using GET and HEAD, as those are the only two http methods that are defined as both safe and cacheable.
Passing headers
Since PUSH_PROMISE frames are required to be cacheable, this could be used to add specific Cache-Control directives to the promise. Section 8.2.2 of the spec states that a client has the option to download the promised stream and can refuse it, which I imagine a client would do if it found that it had an up-to-date version of the resource in its cache.
Controlling caching is the most obvious reason I can see for why you might pass headers, but there may be other reasons as well. If you're writing a custom client, you may use certain X-Headers to provide other hints (that aren't related to caching) to the client so it can decide whether or not it wants to accept the promised stream.
You'll want to pass headers for anything that will cause your response to vary (i.e. anything in your Vary response header). The biggest one I've found is compression.
Read those headers from the original client request and include them with your push promise, e.g.:
var headers = new NameValueCollection { { "accept-encoding", this.Request.Headers["accept-encoding"] } };
this.Response.PushPromise("~/Scripts/jquery.js", "GET", headers);`

HTTP request whose headers can be controlled and is automatically decompressed

I'm trying to send HTTP requests in C# that look like HTTP requests from a certain software. I wanted to use System.Net.HttpWebRequest but it doesn't give me the control I need over its headers: their letter-casing can't be changed (e.g. I want the Connection header to be keep-alive and not Keep-Alive), I don't have full control over the headers ordering, etc.
I tried using HttpClient from CodeScales library. Unfortunately, it doesn't decompress responses automatically (see HttpWebRequest.AutomaticDecompression). I decompressed it myself with System.IO.Compression.GZipStream and DeflateStream, but it didn't work when the response had the header Transfer-Encoding: chunked.
System.Net.Http.HttpRequestHeaders seems to give more control over headers than HttpWebRequest, but still not enough.
How can it be done?
Edit: I know that HTTP accepts those headers as valid anyway, but I'm working with a server that validates the headers and refuses to respond if they're not exactly what it expects.
To set some headers in the HTTPWebRequest class, you have to either use an attribute from the class (for example HttpWebRequest.KeepAlive = true), or you have to add the custom header to the request by calling the add method to the request headers.
Something important is that is you try to add the header (in a custom way) while it's already an attribute of the request, it'll send you an error.
objRequest.Headers.Add("Accept", "some data");
is incorrect. You'd rather say.
objRequest.Accept = "some data";
In your case you can :
objRequest.KeepAlive = true;
Don't worry to much for the letter-casing, it doesn't matter as far as you're sending the appropriate headers to the server.

Categories