How do I get the message from an API using Flurl? - api

I've created an API in .NET Core 2 using C#. It returns an ActionResult with a status code and string message. In another application, I call the API using Flurl. I can get the status code number, but I can't find a way to get the message. How do I get the message or what do I need to change in the API to put the message someway Flurl can get it?
Here's the code for the API. The "message" in this example is "Sorry!".
[HttpPost("{orderID}/SendEmail")]
[Produces("application/json", Type = typeof(string))]
public ActionResult Post(int orderID)
{
return StatusCode(500, "Sorry!");
}
Here's the code in another app calling the API. I can get the status code number (500) using (int)getRespParams.StatusCode and the status code text (InternalError) using getRespParams.StatusCode, but how do I get the "Sorry!" message?
var getRespParams = await $"http://localhost:1234/api/Orders/{orderID}/SendEmail".PostUrlEncodedAsync();
int statusCodeNumber = (int)getRespParams.StatusCode;

PostUrlEncodedAsync returns an HttpResponseMessage object. To get the body as a string, just do this:
var message = await getRespParams.Content.ReadAsStringAsync();
One thing to note is that Flurl throws an exception on non-2XX responses by default. (This is configurable). Often you only care about the status code if the call is unsuccessful, so a typical pattern is to use a try/catch block:
try {
var obj = await url
.PostAsync(...)
.ReceiveJson<MyResponseType>();
}
catch (FlurlHttpException ex) {
var status = ex.Call.HttpStatus;
var message = await ex.GetResponseStringAsync();
}
One advantage here is you can use Flurl's ReceiveJson to get the response body directly in successful cases, and get the error body (which is a different shape) separately in the catch block. That way you're not dealing with deserializing a "raw" HttpResponseMessage at all.

Related

Autowrapper not returning response object when setting status code

I am using auto wrapper in asp.net core and if for a particular id there is no data returned I want to return 204 (NoContent)
[HttpGet]
[Route("GetAccountByAccountNumber/{accountNumber}")]
public async Task<ApiResponse> GetAccountByAccountNumber(string accountNumber)
{
var account = await _accountsService.GetAccountByAccountNumber(accountNumber);
if (account == null)
return new ApiResponse("Account with accountnumber {accountNumber} does nto exists", 204);
return new ApiResponse("Account By Number Returned", account);
}
when I give a call this api i get Error aborted
where as I am expecting usual AutoWrappers response with status code and stuff.
in program.cs
app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions
{
EnableResponseLogging = true,
EnableExceptionLogging = true,
LogRequestDataOnException = true
});
am i missing something?
Update
There is nothing wrong in the DB call. is it expected to return null and is returning null. But when I return Api response I get aborted
in the app insights i see
Response Content-Length mismatch: too few bytes written
Update 2
If I return 200 instead of 204 it works.
I don't think there is a problem with using Autowrapper. I test to create asp.net core3.1 project, using AutoWrapper.Core -Version 4.5.0. Everything works fine.
I think the problem is related to await _accountsService.GetAccountByAccountNumber(accountNumber);,
this line of code.
You can use CancellationToken to check it. For more details, please below blog.
Handling aborted requests in ASP.NET Core

Handle Azure Function input binding error

I have an Azure Function (version 4, with C# on .NET 6) that uses Cosmos DB input binding.
When the Cosmos DB input is querying for a document that does not exist it returns HTTP 404 response, however the Azure Function itself does not propagate this response and actually returns a HTTP 500 response.
How do I catch the Cosmos DB response to be able to act accordingly in the Azure function?
Or in other words - how do I perform error/http-response handling on the function's input bindings?
I hoped validating that the input argument is not null would be it,
and also tried to validate that the IEnumerable response is not empty,
but it turns out it doesn't even enter the code in the function when it gets the Cosmos DB 404 response. The function just breaks and the returns HTTP 500:
[FunctionName("GetItems")]
public IActionResult GetItems(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
[CosmosDB(
databaseName: "my-db",
containerName: "my-container",
Connection = "my-conn-string",
PartitionKey = "my-pk")] IEnumerable<MyItem> items)
{
_logger.LogInformation("Get list of items");
if (items == null)
{
_logger.LogInformation("Items object is null");
return new NotFoundResult();
}
var itemsList = items as List<MyItem>;
if (itemsList.Count == 0)
{
_logger.LogInformation("Items object is missing");
return new NotFoundResult();
}
return new OkObjectResult(itemsList.First());
}
}
Below is the example of input binding error.
[FunctionName("EventHubTrigger")]
[FixedDelayRetry(5, "00:00:10")]
public static async Task Run([EventHubTrigger("myHub", Connection = "EventHubConnection")] EventData[] events, ILogger log)
{
// ...
}
and Here is the complete documentation on Handling binding errors.

How to get status code of HttpCall with Ktor and kotlinx serialization

I am trying to figure out how to check the http status code of a http request with Ktor
I have a simple GET request like this with a HttpResponseObject that holds the data the server returns and any errors server side that I control
val response:HttpResponseObject<MyObject> = client.get<HttpResponseObject<MyObject>>(url)
Now what I need to also be able to check are is if there are unhandled exceptions or Authentication exceptions that get thrown by the server. In these cases nothing would be returned by the server and a status code of 500 or 401 error would be returned.
I see the documentation has you can get the full http response with something like this
val response:HttpResponse client.get(url)
but then how do lose my serialized data coming back and I couldnt find any examples on how to serialize it from the HttpResponse object.
Does anyone have any suggestions? is there a way to get the http status code from my first example?
You can try getting the status code by using the following code:
val response = client.get<HttpResponse>(url) after that, to get the bytes from the response and serialize it you can try using val bytes: ByteArray = response.readBytes()
You can find full documentation here :
https://ktor.io/clients/http-client/quick-start/responses.html
What I ended up doing was using the HttpResponseValidator in the HttpClientConfig to catch the status codes then throw exceptions
HttpResponseValidator{
validateResponse { response: HttpResponse ->
val statusCode = response.status.value
when (statusCode) {
in 300..399 -> throw RedirectResponseException(response)
in 400..499 -> throw ClientRequestException(response)
in 500..599 -> throw ServerResponseException(response)
}
if (statusCode >= 600) {
throw ResponseException(response)
}
}
}
By doing so I was then able to pass the error through my custom object back up to the UI
private suspend fun getCurrentWeatherForUrl(url:String, callback: (HttpResponseObject<MyObject>?) -> Unit){
var response:HttpResponseObject<MyObject>? = null
response = try{
client.get<HttpResponseObject<MyObject>>(url){
header("Authorization", "Bearer $authKey")
}
}catch (e:Exception){
HttpResponseObject(null, e.toString())
}
callback(response)
}
Also you can use HttpResponse.receive() to get a serialized object AND the response data
val response:HttpResponse = client.get(url)
val myObject:MyObject = response.receive<MyObject>()
HttpResponse is deprecated, you need to use HttpStatement and then get the status after calling execute() on it.

RestSharp RestResponse is truncating content to 64 kb

Hi I am using the RestSharp to create the request to my web API. Unfortunately the response.content does not contain full response, which I am able to see when I perform request through browser or fiddler. The content is being truncated to 64 kb. I am attaching my code below.
Could you please advice what could solve this issue?
var request = new RestRequest("Products?productId={productId}&applicationId={applicationId}", Method.GET);
request.RequestFormat = DataFormat.Json;
request.AddParameter("productId", id, ParameterType.UrlSegment);
request.AddParameter("applicationId", Settings.ApplicationId, ParameterType.UrlSegment);
request.AddHeader("X-AppKey", token.AppKey);
request.AddHeader("X-Token", token.Token);
request.AddHeader("X-IsWebApi", "true");
RestResponse response = (RestResponse) client.Execute(request);
if (response.StatusCode == HttpStatusCode.Found)
{
// The following line failes because response.Content is truncated.
ShowProductModel showProductModel =
new JavaScriptSerializer().Deserialize<ShowProductModel>(response.Content);
// Do other things.
return ShowProductApi(showProductModel, q, d, sort, breadcrumb);
}
This is happening because RestSharp uses the HttpWebRequest class from the .NET Framework. This class has a static attribute called DefaultMaximumErrorResponseLength. This attribute determines the max length of an error response, and the default value for this attribute is 64Kb.
You can change the value of that atribbute before instatiating the RestRequest class.
Here's some code:
HttpWebRequest.DefaultMaximumErrorResponseLength = 1048576;
var request = new RestRequest("resource" + "/", Method.POST)
{
RequestFormat = DataFormat.Json,
JsonSerializer = new JsonSerializer()
};
That way your error response can be longer without problemns.
It looks like HttpStatusCode.Found may be causing the issue. That equates to Http Status Code 302 which is a form of redirect. I'm not entirely sure if that's necessarily the right thing to do in this case. If you have "found" the data you are looking for you should return a success level status code, e.g. 200 (Ok). Wikipedia has a list of HTTP Status Codes with summaries about what they mean and links off to lots of other resources.
I've created a little demonstrator solution (You can find it on GitHub) to show the difference. There is a WebApi server application that returns a list of values (Hex codes) and a Console client application that consumes the resources on the WebApi application.
Here is the ValuesFound resource which returns HTTP Status Code 302/Found:
public class ValuesFoundController : ApiController
{
public HttpResponseMessage Get(int count)
{
var result = Request.CreateResponse(HttpStatusCode.Found, Values.GetValues(count));
return result;
}
}
And the same again but returning the correct 200/OK response:
public class ValuesOkController : ApiController
{
public HttpResponseMessage Get(int count)
{
var result = Request.CreateResponse(HttpStatusCode.OK, Values.GetValues(count));
return result;
}
}
On the client side the important part of the code is this:
private static void ProcessRequest(int count, string resource)
{
var client = new RestClient("http://localhost:61038/api/");
var request = new RestRequest(resource+"?count={count}", Method.GET);
request.RequestFormat = DataFormat.Json;
request.AddParameter("count", count, ParameterType.UrlSegment);
RestResponse response = (RestResponse) client.Execute(request);
Console.WriteLine("Status was : {0}", response.StatusCode);
Console.WriteLine("Status code was : {0}", (int) response.StatusCode);
Console.WriteLine("Response.ContentLength is : {0}", response.ContentLength);
Console.WriteLine("Response.Content.Length is: {0}", response.Content.Length);
Console.WriteLine();
}
The count is the number of hex codes to return, and resource is the name of the resource (either ValuesOk or ValuesFound) which map to the controllers above.
The console application asks the user for a number and then shows the length of response for each HTTP Status Code. For low values, say 200, both versions return the same amount of content, but once the response content exceeds 64kb then the "Found" version gets truncated and the "Ok" version does not.
Trying the console application with a value of about 9999 demonstrates this:
How many things do you want returned?
9999
Waiting on the server...
Status was : OK
Status code was : 200
Response.ContentLength is : 109990
Response.Content.Length is: 109990
Status was : Redirect
Status code was : 302
Response.ContentLength is : 109990
Response.Content.Length is: 65536
So, why does RestSharp do this? I've no idea why it truncates content in one instance and not in the other. However, it could be assumed that in a situation where the server has asked the client to redirect to another resource location that content exceeding 64kb is unlikely to be valid.
For example, if you use Fiddler to look at what websites do, the responses in the 300 range (Redirection) such as 302/Found do have a small content payload that simply contain a little HTML so that the user can click the link to manually redirect if the browser did not automatically redirect for them. The real redirect is in the Http "Location" header.

WCF API and API Key authorisation

Written or started to write a WEB API rest service in WCF. It's all going relatively well. However, I've come across a small problem. I've implemented this;
http://blogs.msdn.com/b/rjacobs/archive/2010/06/14/how-to-do-api-key-verification-for-rest-services-in-net-4.aspx
For key validation. (I'm not sure if this is the correct approach for WCF WEB API, since it looks more like the rest service implementation).
Anyway, it seems to work. However, when the api key is not provided the exception is not been displayed in the browser. I.e. if I provide the key, it returns correctly, if I don't it just shows a blank page.
private static void CreateErrorReply(OperationContext operationContext, string key)
{
// The error message is padded so that IE shows the response by default
using (var sr = new StringReader("<?xml version=\"1.0\" encoding=\"utf-8\"?>" + APIErrorHTML))
{
XElement response = XElement.Load(sr);
using (Message reply = Message.CreateMessage(MessageVersion.None, null, response))
{
HttpResponseMessageProperty responseProp = new HttpResponseMessageProperty() { StatusCode = HttpStatusCode.Unauthorized, StatusDescription = String.Format("'{0}' is an invalid API key", key) };
responseProp.Headers[HttpResponseHeader.ContentType] = "text/html";
reply.Properties[HttpResponseMessageProperty.Name] = responseProp;
operationContext.RequestContext.Reply(reply);
// set the request context to null to terminate processing of this request
operationContext.RequestContext = null;
}
}
}
Instead of this showing an error, the result is a blank response. Can anyone help?