Using RestSharp to get header value from HttpResponseMessage in web API? - c#

I'm having some trouble getting a token from a HttpResponseMessage using RestSharp. Here is my API controller code:
public HttpResponseMessage Create(long id)
{
var token = _tokenCreator.CreateToken(id);
var response = new HttpResponseMessage();
response.Headers.Add("Token", token);
response.StatusCode = HttpStatusCode.OK;
return response;
}
This is generating the token like I want, and creating the response pictured below. When I call the method with Postman, this is the body of the response that I receive. This is the same result I get if I look at the Content property of the response in my code that sends the request.
Picture of response body.
There is clearly a header section there, but it's not being recognized by RestSharp in my calling code.
public string CreateToken(long id)
{
var client = new RestClient(apiUrl);
var request = new RestRequest(Method.GET)
{
Resource = "tokencreator/create"
}
request.AddQueryParameter("id", id.ToString());
var response = client.Execute(request);
var headers = response.Headers.ToList();
// Here is what I want to do, but does not return a result
var tokenHeader = headers.Find(x => x.Name == "Token");
if(tokenHeader != null)
{
return tokenHeader.Value.ToString();
}
return "no token";
}
If I loop through and print the response's headers, these are the results:
Transfer-Encoding chunked
X-SourceFiles =?UTF-8?B?QzpccHJvamVjdHNcYXBpXEVudGl0bGVtZW50QWNjZXNzXHRva2VuY3JlYXRvclxjcmVhdGU=?=
Content-Type application/json; charset=utf-8
Date Mon, 03 Apr 2017 16:00:18 GMT
Server Kestrel
X-Powered-By ASP.NET
The "Token" header I added in the controller to the response is not there. Is there a simple way to access the "header" section that is appearing in the body of the response?
Edit: Attaching a picture of the "Headers" section of the response from Postman. There is no "Token" header. Any idea why the response.Headers.Add("Token", token) method does not add the header, or am I misunderstanding headers completely?
Picture of headers in Postman response.

So I should have specified that this was a .NET Core application. To fix the problem, I had to go to the Startup.cs file and add the following to the ConfigureServices method:
Change services.AddMvc(); to services.AddMvc().AddWebApiConventions();
You will need the NuGet package Microsoft.AspNetCore.Mvc.WebApiCompatShim, which allows compatibility with the old Web Api 2 way.

Related

Difficulty receiving an HTTP Response from API -- Bad Request Error

I'm attempting to pass username/password from an application to the API to receive a token authorization key. When I attempt to do so, I receive a 400 Bad Request error and I cannot figure out why. Below is the method in question:
public User UserAuthentication(string username, string password)
{
string endpoint = baseURL + "/TOKEN";
// Could be POST maybe
string method = "POST";
Credential jsonObj = new Credential
{
grant_type = "password",
username = username,
password = password
};
string jsonStr = JsonConvert.SerializeObject(jsonObj);
WebClient wc = new WebClient();
//x - www - form - urlencoded
wc.Headers[HttpRequestHeader.ContentType] = "application/x - www - form - urlencoded";
wc.Headers.Add("Access-Control-Allow-Headers", "content-type");
wc.Headers.Add("Access-Control-Allow-Origin", "*");
wc.Headers[HttpRequestHeader.Authorization] = "Bearer <token>";
wc.Headers.Add("Access-Control-Allow-Methods", "POST, PUT, GET, DELETE, OPTIONS");
string header = wc.Headers.ToString();
try
{
string response = wc.UploadString(endpoint, method, jsonStr);
return JsonConvert.DeserializeObject<User>(response);
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
I've messed around altering just about everything in this method in search of a fix.
What I've done:
/TOKEN was /values & /api/values
POST method was GET -- With this, I received a "Cannot send a content-body with this verb-type." error.
ContentType was changed to "application/json"
Access-Control-Allow-Origin had the baseURL
Checked the format of header & body:
Header:
{Content-Type: application/x - www - form - urlencoded
Access-Control-Allow-Headers: content-type
Access-Control-Allow-Origin: *
Authorization: Bearer <token>
Access-Control-Allow-Methods: POST, PUT, GET, DELETE, OPTIONS}
Body:
{"grant_type":"password",
"username":"test#gmail.com",
"password":"password123"}
I obviously have something wrong in my request, I've just run out of ideas to try. I'm not entirely sure if UploadString() is the correct method to be using in this situation, but I couldn't find another method in the WebClient class that would be better. Any help to try and push me in the right direction would be very much appreciated.
So what I think you are trying to do is a form-urlencoded post to a "token" endpoint with a username/password grant. These are typically done like so:
using (var request = new HttpRequestMessage(HttpMethod.Post, new Uri("https://example.com/token"))
{
Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "password" },
{ "username", "username#site.com" },
{ "password", "password12345" }
})
})
{
using (var resp = await _client.SendAsync(request))
{
resp.EnsureSuccessStatusCode();
//await resp.Content.ReadAsAsync<BearerToken>();
// for testing purposes, try this:
var returnData = await resp.Content.ReadAsStringAsync();
Console.WriteLine(returnData);
}
}
You should define this outside all scopes where you need to do Http requests:
private static readonly HttpClient _client = new HttpClient();
So, first off, try to stick with HttpClient. Other patterns such as WebClient are considered legacy.
Next, CORS headers are typically returned from the server when an OPTIONS call is sent to the server. You aren't doing that here, and you should never have to worry about that kind of stuff inside a C# program running from your computer. So you can drop the access-control header stuff.
Form-urlencoded data is not JSON data. It's a different way to format data. If you want to send JSON data, you should use the content-type application/json
Finally, you are trying to add an Authorization header. But that doesn't make much sense as you are trying to authenticate yourself to become authorized. If you send the right username/password, you will receive a bearer token that you can use in an Authorization header for future requests to said service.
Oh and I forgot to add: Whenever you see an error in the [400,499] range (in this case "400 - bad request") it means that you sent something wrong and the server doesn't understand what you are trying to do. For example: a 401 means you sent invalid or missing authorization information. A 400 means your data was probably malformed.
But I like your question... I can see what you were doing and you tried all kinds of different things.
Download a program called Fiddler if you want to see how HTTP works. It's a great tool to debug your HTTP calls.

HttpClient.PostAsJsonAsync not working or giving any errors in debug mode

I’m having some problems using the Web API with the MVC, not sure what is causing it but it doesn’t throw any exceptions or errors in debug mode, please could someone help to resolve this issue.
Code is as follows:
MVC Controller calls:
PortalLogonCheckParams credParams = new PortalLogonCheckParams() {SecurityLogonLogonId = model.UserName, SecurityLogonPassword = model.Password};
SecurityLogon secureLogon = new SecurityLogon();
var result = secureLogon.checkCredentials(credParams);
Data Access Object method:
public async Task <IEnumerable<PortalLogon>> checkCredentials(PortalLogonCheckParams credParams)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:50793/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Check Credentials
//Following call fails
HttpResponseMessage response = await client.PostAsJsonAsync("api/chkPortalLogin", credParams);
if (response.IsSuccessStatusCode)
{
IEnumerable<PortalLogon> logonList = await response.Content.ReadAsAsync<IEnumerable<PortalLogon>>();
return logonList;
}
else return null;
}
}
Web API:
[HttpPost]
public IHttpActionResult chkPortalLogin([FromBody] PortalLogonCheckParams LogonParams)
{
List<Mod_chkPortalSecurityLogon> securityLogon = null;
String strDBName = "";
//Set the database identifier
strDBName = "Mod";
//Retrieve the Logon object
using (DataConnection connection = new DataConnection(strDBName))
{
//Retrieve the list object
securityLogon = new Persistant_Mod_chkPortalSecurityLogon().findBy_Search(connection.Connection, LogonParams.SecurityLogonLogonId, LogonParams.SecurityLogonPassword);
}
AutoMapper.Mapper.CreateMap<Mod_chkPortalSecurityLogon, PortalLogon>();
IEnumerable<PortalLogon> securityLogonNew = AutoMapper.Mapper.Map<IEnumerable<Mod_chkPortalSecurityLogon>, IEnumerable<PortalLogon>>(securityLogon);
return Ok(securityLogonNew);
}
You need to remove the [FromBody] attribute from the parameter
Using
[FromBody]
To force Web API to read a simple type from the request body, add the
[FromBody] attribute to the parameter:
public HttpResponseMessage Post([FromBody] string name) { ... }
In this example, Web API will use a media-type formatter to read the
value of name from the request body. Here is an example client
request.
POST http://localhost:5076/api/values HTTP/1.1
User-Agent: Fiddler
Host: localhost:5076
Content-Type: application/json
Content-Length: 7
"Alice"
When a parameter has [FromBody], Web API uses the Content-Type header
to select a formatter. In this example, the content type is
"application/json" and the request body is a raw JSON string (not a
JSON object).
At most one parameter is allowed to read from the message body.

SurveyMonkey API - Authorization Token Not Found

I built a C# application about 8 months ago to pull SurveyMonkey responses and store them on our SQL Server. The application has run every day for over 6 months without any issue, but suddenly Friday morning I can't get any requests to go through. I'm not seeing anything on the developer site mentioning outages, so I've been examining my code for possible issues.
This is the first app I've ever written to create web requests, so it's possible I'm doing things badly.
Request, from Fiddler:
POST https://api.surveymonkey.net/v2/surveys/get_survey_list?api_key=<key hidden> HTTP/1.1
Authorization: bearer <token hidden>
Content-Type: application/json
Host: api.surveymonkey.net
Content-Length: 146
Expect: 100-continue
Connection: Keep-Alive
{
"page": 1,
"fields": ["title","analysis_url","preview_url","date_created","date_modified","language_id","question_count","num_responses"]
}
Response body, from Fiddler:
{"status":1,"errmsg":"Request header \"Authorization\" token not found"}
C# code:
private JObject SubmitPostRequest(string URIPath, string RequestBody)
{
using (var webClient = new WebClient())
{
// Create URI for POST request to go to. URI varies depending on API method and includes API Key.
Uri requestURI = new Uri(APIHost, URIPath + "?api_key=" + APIKey);
// Have web client use NT credentials for proxy server
webClient.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
// Specify headers: access token in authorization header, content type
webClient.Headers["Authorization"] = "bearer " + AccessToken;
webClient.Headers["Content-Type"] = "application/json";
// Create requestBody as byte[]
byte[] requestBody = Encoding.Default.GetBytes(RequestBody);
// Connect to the URI and send requestBody as a POST command.
byte[] postResponse = webClient.UploadData(requestURI, "POST", requestBody);
// Update LastConnect
LastConnect = DateTime.Now;
++TransactionCounter;
// Convert byte[] response to string, parse string and return as JObject.
JObject jsonResponse = JObject.Parse(Encoding.Default.GetString(postResponse));
// Evaluate "status" field of jsonResponse. Throw exception if response is not 0.
dynamic dyn = jsonResponse;
if (dyn.status != 0)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("HTTP Post request failed:");
sb.Append("\tstatus: ").AppendLine(dyn.status.ToString());
sb.Append("\terrmsg: ").AppendLine(dyn.errmsg.ToString());
throw new WebException(sb.ToString());
}
return jsonResponse;
}
}
It took awhile, but I managed to get in touch with someone in corporate IT who monitored the traffic to see what was going on. They implemented a fix to the proxy server and everything is finally working again.

Return XML content on WebAPI OWIN for Twilio

I'm making a pretty simple WebAPI using OWIN/Katana self-hosted in a Azure Worker role. Everything is fine from the host perspective since I receive the request and it is routed to my Action just fine.
The problem is that the action MUST return a XML for the API caller/invoker and it is return a wrong encoding string as follow:
RAW Response:
HTTP/1.1 200 OK
Content-Length: 150
Content-Type: text/xml; charset=utf-8
Server: Microsoft-HTTPAPI/2.0
Date: Wed, 16 Jul 2014 05:49:42 GMT
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/"><Response>
<Say>Hello World</Say>
</Response></string>
RAW Request:
GET http://localhost:81/v1/ivr/menu?from=+12345&to=645645&callsid=11111111 HTTP/1.1
User-Agent: Fiddler
Host: localhost:81
Content-Type: text/xml;
response.ToString() result:
<Response>
<Say>Hello World</Say>
</Response>
API Controller Action code:
[HttpGet]
[Route("menu")]
public IHttpActionResult Menu(string from, string to, string callSid)
{
var response = new TwilioResponse();
response.Say("Hello World");
return Ok(response.ToString());
}
Well, all I need is to return the XML as this:
<Response>
<Say>Hello World</Say>
</Response>
What exactly am I doing wrong? The response is coming between tags and with a weird xmlns with what seems to be a wrong encoding...
I've tried to add Content-Type and Accept headers on the request to text/xml(I can't use application/xml but even if I do, it don't changes the output response)...
Also I've tried to create a OWINMiddleware that force the Response content type to text/xml as this:
public class XmlResponseMiddleware : OwinMiddleware
{
public XmlResponseMiddleware(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
context.Response.ContentType = "text/xml";
await this.Next.Invoke(context);
}
}
No lucky...
Any help would be appreciated.
Thanks!
[HttpGet]
[Route("menu")]
public HttpResponeMessage Menu(string from, string to, string callSid)
{
var response = new TwilioResponse();
response.Say("Hello World");
return new HttpResponseMessage()
{
Content = new StringContent(
response.ToString(),
Encoding.UTF8,
"text/xml"
)
};
}
I know this was already answered, but the answer required changing your response type from an IHttpActionResult to an HttpResponeMessage.
If you want to preserve the IHttpActionResult (and make it a little cleaner) you can do this:
using System.Xml.Linq;
[HttpGet]
[Route("menu")]
public IHttpActionResult Menu(string from, string to, string callSid)
{
var response = new TwilioResponse();
response.Say("Hello World");
return Ok(XElement.Parse(response.ToString()));
}
In your WebApiConfig, add this to the register.
It will cause XML to be the default.
Why Twilio doesn't include application/xml as an accept header is beyond me.
config.Formatters.Clear();
config.Formatters.Add(new XmlMediaTypeFormatter());
config.Formatters.Add(new JsonMediaTypeFormatter());
config.Formatters.Add(new FormUrlEncodedMediaTypeFormatter());

Get Response with HttpClient

I'm trying to use HttpClient to read the response content from a 3rd party API (Rackspace Cloud Files). Here's what I have so far. I can't seem to get the content.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Auth_User", username);
client.DefaultRequestHeaders.Add("X-Auth-Key", api);
client.GetAsync("identity.api.rackspacecloud.com".ToAbsoluteUrl()).ContinueWith(
(requestTask) =>
{
HttpResponseMessage response = requestTask.Result;
response.EnsureSuccessStatusCode();
response.Content.ReadAsAsync<string>().ContinueWith(
(readTask) =>
{
var result = readTask.Result;
});
});
This gives me "No 'MediaTypeFormatter' is available to read an object of type 'String' with the media type 'text/html'." error.
I need to retrieve the response details as noted in the Rackspace docs (example):
HTTP/1.1 204 No Content
Date: Mon, 12 Nov 2007 15:32:21 GMT
X-Storage-Url: https://storage.clouddrive.com/v1/CF_xer7_34
X-CDN-Management-Url: https://cdn.clouddrive.com/v1/CF_xer7_34
X-Auth-Token: eaaafd18-0fed-4b3a-81b4-663c99ec1cbb
Content-Length: 0
Content-Type: text/plain; charset=UTF-8
How do I get the response?
When I use ReadAsStringAsync, it gives my the HTML source of my page.
Thank you.

Categories