Get HTTP StatusCode in WebException - vb.net

I am wondering how can I get the HTTP Status returned after a WebException was thrown. I am calling for example a RestAPI to get a token and the Server returns a 401 and a Body in json Format telling me that access is denied. I would like to get the 401 but have not found a way to only get 401.
Catch ex As WebException
Dim resp = New StreamReader(ex.Response.GetResponseStream()).ReadToEnd()
Dim errorNumber As Integer = CInt(ex.Status)
Console.WriteLine(ex.Message & " " & errorNumber)
Console.WriteLine(resp & " ")
Return resp
Below is the console output I have for my code:
CInt(ex.Status) = "7" and the
ex.message = "The remote server returned an error: (401) Unauthorized."
What I am looking for is to get the 401 or whatever the Server sends which would be equal to response.StatusCode

I actually found a way to access the 401 directly
Dim ExResponse = TryCast(ex.Response, HttpWebResponse)
Console.WriteLine(ExResponse.StatusCode)

I have the same problem and I realise a few things while I search for a solution.
WebExceptionStatus enum is not equivalent to http status code that the API you call returned. Instead it is a enum of possible error that may occour during a http call.
The WebExceptionStatus error code that will be returned when you receive an error (400 to 599) from your API is WebExceptionStatus.ProtocolError aka number 7 as int.
When you need to get the response body or the real http status code returned from the api, first you need to check if WebExceptionStatus.Status is WebExceptionStatus.ProtocolError. Then you can get the real response from WebExceptionStatus.Response and read its content.
This is a C# code but you folow the same logic in VB.Net
try
{
...
}
catch (WebException webException)
{
if (webException.Status == WebExceptionStatus.ProtocolError)
{
var httpResponse = (HttpWebResponse)webException.Response;
var responseText = "";
using (var content = new StreamReader(httpResponse.GetResponseStream()))
{
responseText = content.ReadToEnd(); // Get response body as text
}
int statusCode = (int)httpResponse.StatusCode; // Get the status code
}
// Handle other webException.Status errors
}

Related

How to add 'version' field to Content-Type Header in VB.NET

I'm implementing communication with an API REST for my Visual Basic .Net application. The issue appears when I try to add the field version=1 to the Content-Type header. Here's the code I use in order to do it:
Public Function AddTercero(tercero As Tercero, connection As GestionaConnection) As Boolean
If connection Is Nothing Then
Throw New ArgumentNullException(NameOf(connection))
End If
If tercero Is Nothing Then
Throw New ArgumentNullException(NameOf(tercero))
End If
Dim request = New HttpRequestMessage()
request.RequestUri = New Uri(Convert.ToString(connection.RecursosDictionary("vnd.gestiona.thirds"), CultureInfo.CurrentCulture))
request.Method = HttpMethod.Post
request.Headers.Add("X-Gestiona-Access-Token", AccessToken)
request.Headers.Add("Accept", "application/json")
request.Content = New StringContent(JsonConvert.SerializeObject(tercero))
request.Content.Headers.ContentType = New MediaTypeHeaderValue("application/vnd.gestiona.third+json; version=1")
Dim req = request.Content.ReadAsStringAsync
Dim response As HttpResponseMessage = connection.Connection.SendAsync(request).Result
request.Dispose()
Dim resultado = response.Content.ReadAsStringAsync()
Debug.WriteLine(resultado.Result.ToString)
If response.StatusCode = HttpStatusCode.Created Then
Return True
ElseIf response.StatusCode = HttpStatusCode.Unauthorized Then
Throw New InvalidOperationException("Error al añadir tercero: no tiene autorización " & response.ReasonPhrase)
Else
Throw New InvalidOperationException("Error al añadir tercero: " & response.StatusCode & " -> " & response.ReasonPhrase)
End If
End Function
The error message says:
System.InvalidOperationException: Error al añadir tercero: 415 -> Unsupported Media Type
And the message I get from the server is:
{
"code": 415,
"name": "Unsupported Media Type",
"description": "Content not supported: application/vnd.gestiona.third+json",
"technical_details": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.16"
}
I have spoken with the developers of the API and they say I must include the version field on the Content-Type header, here's what they said:
Content-Type with version: application/vnd.gestiona.third+json; version=1
Any ideas on how could I solve this problem?
Thank you for reading
I solved it by declaring the application/vnd.gestiona.third+json content type and the version=1 type separately as it follows:
request.Content.Headers.ContentType = New MediaTypeHeaderValue("application/vnd.gestiona.third+json")
request.Content.Headers.ContentType.Parameters.Add(New NameValueHeaderValue("version", "1"))
Instead of doing it as I was:
request.Content.Headers.ContentType = New MediaTypeHeaderValue("application/vnd.gestiona.third+json; version=1")
This way, it works like a charm.

Getting (401) UnAuthorized error on some requests not all, but most

I don't think this is a code issue, but we have a list of hundreds of addresses to process. Some return data and we are able to get Long and Lat but most return (401) UnAuthorized errors. What would cause this to happen? We have tried passing Host Headers and everything else, the REST API seems to work better in our development environment but throws way more errors when deployed to our Job Server. Any help on this issue will be greatly appreciated. We would like to understand why some calls work and others don't, we pass the same apiKey each time so this is really confusing. Thanks
Here is a code snippet using c# (Work in progress):
//GET THE LATITUDE AND LONGITUDE BASED OFF THE PHYSICAL ADDRESS
String clientAddress = clientRow["home_address"].ToString() + ", " + clientRow["home_city"].ToString() + ", " + clientRow["home_state"].ToString() + ", " + clientRow["home_zip"].ToString();
Logger.Debug("CLIENT ADDRESS: {0}", clientAddress);
String geocoderUri = "https://geocode.search.hereapi.com/v1/geocode?q=" + clientAddress + "&apiKey=xxxxxxxxxxxxxxxxxxxxx"; //KEY REMOVED FOR POSTING ON STACK OVERFLOW
var syncClient = new WebClient();
var content = syncClient.DownloadString(geocoderUri);
//CREATE THE JSON SERIALIZER AND PARSE OUR RESPONSE
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(AddressData));
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(content)))
{
var addressData = (AddressData)serializer.ReadObject(ms);
if (addressData.items.Count() > 0)
{
//INSERT THE LATITUDE AND LONGITUDE IN DB
String sLat = addressData.items[0].position.lat.ToString();
String sLong = addressData.items[0].position.lng.ToString();
Logger.Debug("CLIENT GEOLOCATION - Longitude: {0} Latitude: {1}", sLong, sLat);
insertLatLong(sLat, sLong, clientRow["clientID"].ToString(), 1);
}
}
Would you please try to use RestSharp lib for rest api?
Please see below sample code.
var client = new RestClient("https://geocode.search.hereapi.com/v1/geocode?q="+ clientAddress);
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer YOUR TOKEN");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);

GDAX Post Call returns invalid signature

I am trying to make a post request on GDAX.
But I always receive a "invalid signature" message.
GDAX API Docs for creating request + signing: https://docs.gdax.com/#creating-a-request
Preshash string returns the following:
1500627733POST/orders{"price":"1000.0","size":"0.02","type":"limit","side":"sell","product_id":"BTC-EUR"}
My signature method:
public String generateSignature(String requestPath, String method, String body, String timestamp) {
try {
String prehash = timestamp + method.toUpperCase() + requestPath + body;
byte[] secretDecoded = Base64.getDecoder().decode(secretKey);
SecretKeySpec keyspec = new SecretKeySpec(secretDecoded, "HmacSHA256");
Mac sha256 = (Mac) Mac.getInstance("HmacSHA256").clone();
sha256.init(keyspec);
return Base64.getEncoder().encodeToString(sha256.doFinal(prehash.getBytes()));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
My request method:
private boolean placeLimitOrder(String currencyPair, String side, String price, String size)
throws UnirestException {
String timestamp = Instant.now().getEpochSecond() + "";
String api_method = "/orders";
String path = base_url + api_method; //base_url = https://api.gdax.com
String method = "POST";
String b = "{\"price\":\"1000.0\",\"size\":\"0.02\",\"type\":\"limit\",\"side\":\"sell\",\"product_id\":\"BTC-EUR\"}";
JsonNode n = new JsonNode(b);
String sig = generateSignature(api_method, method,b, timestamp);
HttpResponse<JsonNode> rep = Unirest.post(path).header("accept", "application/json")
.header("content-type", "application/json")
.header("CB-ACCESS-KEY", publicKey)
.header("CB-ACCESS-PASSPHRASE", passphrase)
.header("CB-ACCESS-SIGN", sig)
.header("CB-ACCESS-TIMESTAMP", timestamp)
.body(n)
.asJson();
System.out.println(rep.getStatusText()); //Bad Request
System.out.println(rep.getBody().toString()); //invalid signature
System.out.println(sig); //returns something
return false;
}
I also tried to make a API Request Call with Insomnia but it returns the same message ("invalid signature").
Any clues?
Thank you very much in advance!
Looks like you are signing the price order data which is a string, but for the body in the post you are turning it into a json node. Which may not match when gdax decodes the signing and compares the payload data to the decrypted(signed body) when they receive it.
Why not just send the string as the body and remove the ".asJson"?
.body(b)
I was stuck on a similar issue when I was testing the API in C#. After 3 afternoons of trying. I tested sending the data as a string and I was able to get pass the invalid signature error.
I had the same problem.
I used http:
but the right one httpS:
Problem solved.

C# "The request was aborted: Could not create SSL/TLS secure channel." - happening occasionally

This is a request to GoCardless test API from a Dynamics CRM plugin. I receive "The request was aborted: Could not create SSL/TLS secure channel." error. It only happens on the first request after some time without sending one. If I send it again, it will be OK. I would appreciate a lot your help.
Here is my code:
//I have tried all the following lines in comment without success
//ServicePointManager.ServerCertificateValidationCallback += ValidateRemoteCertificate;
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
//ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
//ServicePointManager.Expect100Continue = true;
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
// Create a new WebClient instance.
string baseURL = "https://api-sandbox.gocardless.com/";
WebClient client = new WebClient();
client.Headers.Add("Content-Type", "application/json");
client.Headers.Add("Authorization", "Bearer " + t);
client.Headers.Add("GoCardless-Version", "2015-07-06");
client.Headers.Add("Accept", "application/json");
Customers model = new Customers();
customer.country_code = "GB";
model.customers = customer;
MemoryStream stream1 = new MemoryStream();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Customers));
ser.WriteObject(stream1, model);
stream1.Position = 0;
StreamReader sr = new StreamReader(stream1);
// Apply ASCII Encoding to obtain the string as a byte array.
byte[] byteArray = Encoding.ASCII.GetBytes(sr.ReadToEnd());
ReturnedCustomers result = new ReturnedCustomers();
//Upload the input string using the HTTP 1.0 POST method.
try
{
byte[] responseArray = client.UploadData(baseURL + "customers", "POST", byteArray);
string responseText = Encoding.ASCII.GetString(responseArray);
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(ReturnedCustomers));
using (Stream s = GenerateStreamFromString(responseText))
{
result = (ReturnedCustomers)serializer.ReadObject(s);
}
}
catch (WebException exception)
{
}
From the Microsoft documentation (https://msdn.microsoft.com/en-us/library/gg334752.aspx) are the following limitations:
Only the HTTP and HTTPS protocols are allowed.
Access to localhost (loopback) is not permitted.
IP addresses cannot be used. You must use a named web address that requires DNS name resolution.
Anonymous authentication is supported and recommended.
5.There is no provision for prompting the logged on user for credentials or saving those credentials.
The error may be due to seguneti things:
The certificate is invalid
The certification authority is not public
Could you check what is the value of ServicePointManager.Expect100Continue and ServicePointManager.SecurityProtocol attributes in your environment?

Changing httpresponse status on netty

The api is streaming large volume of data. after performing validation and opening up the connection to backend then creating the jdbc statement, we return httpresponse ok status with the header. The problem we see is that when the streaming breaks, the client does not get error code and only thing we can do is just close the channel.
Here is how we send the status back at the begining;
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
response.headers().set(CONTENT_TYPE, MimeTypes.TEXT_JSON_UTF_8);
response.setChunked(true);
response.headers().set(Names.TRANSFER_ENCODING, Values.CHUNKED);
Channel ch = ctx.getChannel();
// Write the initial line and the header.
ch.write(response);
When anything fails during the streaming, the error is captured by the catch block;
} catch (Exception e) {
ctx.getChannel().close();
String msg = "Error while streaming dynamic content from backend datasource " + ((DatasetDynamic) datasets[0]).getDbDataSourceName();
error(e, msg);
debug("uriNodes=" + this.uriNodes + "; params=" + this.params);
throw new Exception(msg, e);
} finally {
As you see in the catch block, to notify the client, something went wrong, all it is doing is;ctx.getChannel().close();
Is there anyway we can send proper httpresponse with error back to the client?
Looks like you can send httpresponse anytime thru the channel, it does not have to be as header;
} catch (Exception e) {
// Any exception here means an error in the middle of chunk streaming, we
// send back http 500 to the client to inform the failure
ResponseErrorStatus status = new ResponseErrorStatus(ServiceErrorStatus.INTERNAL_SERVER_ERROR,
" error: " + e.getMessage() + (e.getCause() != null ? (" - caused by: " + e.getCause().getMessage()) : ""));
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status.serviceErrorStatus.httpStatus);
Channel ch = ctx.getChannel();
ch.write(response);