Http Header Throws Exception C# HttpResponseMessage - c#

Okay I'm developing a sevice application that runs on Cisco IP Phones. The application involves displaying messages to the phone.
I would like to make use of the way CISCO phones use the Expires header in a http Response. Basically a message i send to the phone will expire when the time specified in the header is reached (expired messages are removed from the message stack). The full documentation can be read at this address
http://www.cisco.com/en/US/docs/voice_ip_comm/cuipph/all_models/xsi/3_3_4/english/programming/guide/ip334ch5.html#wp1030621
In my C# WebService i construct the response using a HttpResponseMessage. Before i return my response i add the Expires header using
response.Headers.Add("Expires", "-1"); //Immediately expires.
My problem:
The previous line of code throws an InvalidOperationException
with the message Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage
I believe that the HttpResponseMessage is performing some validation and that Expires is not a valid response header. But its what the CISCO stuff requires.
Can i force this key value into the header even though its not strictly correct HTTP

The Expires header is on the Content object.
response.Content.Headers.Expires = new DateTimeOffset(DateTime.UtcNow.Add(new TimeSpan(0,0,0,5)));

Related

C# HttpClient authorization header removed after send to server

I wanna send request to external API through the HttpClient from my own API in c#.
I am using dot net 5 and basic authentication.
Here is my code:
var client = new HttpClient
{
BaseAddress = new Uri(baseUrl)
};
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Put, "apiUrl");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var param = JsonConvert.SerializeObject(new
{
param1="",
param2=""
});
requestMessage.Content = new StringContent(param, Encoding.UTF8, "application/json");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes($"{user}:{pass}")));
HttpResponseMessage response = await client.SendAsync(requestMessage);
Usually, I send http request like this.
but now I have some problem.
After line HttpResponseMessage response = await client.SendAsync(requestMessage); authorization header removed from my request header and I got UnAuthorized http error.
I know that when redirection occurs, authorization header removed for security reason. Also I'm not sure about redirect in external API.
I add the HttpClientHandler with AllowAutoRedirect = false to my HttpClient
var handler = new HttpClientHandler()
{
AllowAutoRedirect = false,
};
var client = new HttpClient (handler)
{
BaseAddress = new Uri(baseUrl)
};
Now I got Redirect error 301(Permanently moved).
I decided to test the code in Postman. By default, when I call API in Postman, I got http error 405 method not allowed and error detail like this:
{
"detail": "Method "GET" not allowed."}
External API method is PUT. but here I got GET Error.
I tried many options in postman and finally I find the option in Postman:
When I toggle it on, external API work's properly.
Also I test it with Insomnia and it's work properly.
Does it related to my code or dot net 5 or what other thing in my code or it's related to external API?
If it's related to my code, How can I solve the error?
If error related to external API, why Postman and Insomnia response is ok?
External API has Core Policy for specific domain and I send request from other domain.
All I Know is that the CORS policy applied in browser. not in Postman, Insomnia or C# Code.
What about CORS? Does it related to CORS? if Yes, what shall I do?
I will be grateful for your answer.
Update
I detect WWW-Authenticate: JWT realm="api" in the response header.
What exactly is it? and what shall I do?
I find out the problem. It's really Ridiculous.
When I use URL like www.abc.com/api/something the request gets 301 error and postman sends another request like www.abc.com/api/something/. The difference is just / at the end of request.
I tried new URL in postman and first request got ok.
Also I tried URL in my C# code and again its ok.
But i could not understand why.
Thanks a lot dear #pharaz-fadaei
You are right about the removal of authorization headers after redirects. But keep in mind that this behavior is part of the design of the HttpClient in C#. Postman and Insomnia may have different mechanisms to send the authorization headers on each consecutive request that is caused by redirects. The option that you enabled in Postman will make it use the original HTTP method you specified (PUT) as the HTTP method to send further requests based on the redirect messages (Postman uses GET method by default on requests instructed by redirect messages).
The fact that you see a 301 shows that a redirection is required. You can check Location header value in response.Headers to see the real location and send your requests with the authorization headers to that endpoint directly. If I were you I wouldn't use the new location directly because the original endpoint is what you were given by the authors of the API. Instead I would programmatically send the request to the original endpoint and resend the request on 301 codes to the new Location (use PUT method due to the behavior of Postman) until you get the result. This answer can give you some ideas: https://stackoverflow.com/a/42566541/1539231
Since you see WWW-Authenticate: JWT realm="api" header, the external API is required a JWT token authentication, not basic authentication. I think first you might need to check external api's documentation.

ServiceStack custom response on failed authentication

I've created a custom authentication for servicestack, which works well. The only problem is, that I get empty responses for every route, that requires authentication, when I am not logged in. How can I change this to return something like
{
"statuscode":"401",
"message":"Unauthorized"
}
Thanks!
The Status Code and the Status Description is already in the returned HTTP Response Headers which is the expected response from a HTTP API. If you're calling from a web browser (i.e. client that accepts HTML) you can implement a /login page (configurable with AuthFeature.HtmlRedirect) to show the user a login page.
Otherwise you can override OnFailedAuthentication() in your Custom AuthProvider to override what gets returned in a failed Auth response, be mindful of what you write in the response body as a JSON response only makes sense for clients requesting JSON responses.

Check url with NTLM on server side

I need to check if url exists and can be reached. In order to do it I send Get request and handle the status:
var httpClient = new HttpClient();
var response = httpClient.GetAsync(new Uri(pageUrl));
isPageAccessible = response.Result.StatusCode == HttpStatusCode.OK;
However, the server uses NTLM for authentication. As I found it here, there are several steps (requests) before I get success OK status. For the first request I get 401 Unauthorized status and can't go to further steps.
All in all, how can I check url on the server with NTML upon completion of all requests?
If you are accessing an authenticated server, you should provide credentials. Credentials of running process for NTLM can be provided with HttpClient as below:
var handler = new HttpClientHandler {
Credentials = System.Net.CredentialCache.DefaultCredentials
};
var httpClient = new HttpClient(handler);
var response = httpClient.GetAsync(new Uri(pageUrl));
You're setting yourself up for failure since there are dozens of reasons why a request may not return a 200 OK response. One may be that the response has no content 204 No Content. Another may be that the request only accepts POST or PUT requests. Another, as you've discovered, may be that it has an authentication system in front of it 401 Not Authorized. Another may be just that the response is a redirect 301 Moved Permanently or 302 Found. Or it could be behind a proxy 305, 306, etc.
The only way you can determine if a URL really exists is to request that the other end prove it. Google does it, Facebook does it, Pinterest does it, etc. The way they do it is they ask the sender to set an MX record in their DNS or a meta tag on their index.html with a custom token they generate. If the token exists, then they're who they say they are.
Anything else is unreliable.

Get Host of HttpResponseMessage in Windows Store App

I have a Windows Store App (C#) where I am sending a HttpRequest and I want to check if the response I am getting is from a Captive/Limited Access Network or from the actual host specified in the HttpRequest.
So lets say I am sending a request to www.serverA.com
I look at the response of that request and determine if it was success based on the status code.
Imagine the same scenario in a captive network(airport networks/starbucks where they redirect you to a login page):
I am sending a request to www.serverA.com
My request gets redirected
to www.serverB.com/AirPortLoginPage
I get back a response that the
AirportLoginPage loaded successfully with a 200 response
My code sees that as a success because of the 200 status code, but I wanted to know if my original request was successful
So, is there a way to determine the host of the server where the Response Message is coming from?
Two things which can solve your problem:
You can set HttpClientHandler.AllowAutoRedirect property to
false. But if any other code depends on this - you will need to
handle 3xx (redirect) manually.
You can check HttpResponseMessage.RequestMessage. In your example after you will send request to www.serverA.com - this property will have www.serverB.com/AirPortLoginPage

How to obtain header values from Client Message Inspector in WCF

I'm creating a web test client for some services I'm working on, and as part of the requirements for that, I would like to be able to display the full request and response SOAP messages (and HTTP headers to the user).
I implemented a MessageInspector class implementing IClientMessageInspector, most notably the BeforeSendRequest and AfterReceiveReply methods to get access to the request & response messages respectively.
Capturing the response (AfterReceiveReply) works great, but capturing the request only partially works. I can access most of the message, however the SOAP header and HTTP headers are both empty. Viewing the request in Fiddler, I can see that WCF is sending a Security header in the SOAP message and "a bunch" of HTTP headers.
My BeforeSendRequest method is very simple...the gist of it is...
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
this.RequestMessage = request.ToString(); // Security header is missing from message
// Try to get HTTP headers
object req; // req is always null
if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out req))
{
this.RequestHeaders = ((HttpRequestMessageProperty)req).Headers;
}
return null;
}
I'm not sure why the HTTP and Security headers are missing. Is there a better way to do this?
Wayne
The inspectors look at the messages right after the message exists the formatter, and before it reaches any of the protocol channels (such as security) which will (potentially) change the message before passing it over (see the diagram in the post about WCF channels at https://learn.microsoft.com/en-us/archive/blogs/carlosfigueira/wcf-extensibility-channels). So at the inspector level you won't be able to find any additional SOAP headers added by the message. The HTTP headers are added by the transport which is also reached after the message passes through the message inspector.
If you want to see all the SOAP headers in the message, you can either create a new "protocol" channel (the sample at http://msdn.microsoft.com/en-us/library/ms751495.aspx does exactly that) or a new message encoder (it can wrap the existing encoder, and inspect the outgoing messages right before they're encoded).
To be able to see the HTTP headers it's harder, since the transport is the last part through which the message passes in WCF. I think you could write a custom transport channel to do that, but that would definitely be a lot of code.

Categories