Deserialize HttpResponseMessage - c#

I have an API (asp net core 3.1) and a web application (asp net Blazor WebAssembly).
In my API I have a CustomerController with the following method for POST:
[HttpPost]
public async
Task<ActionResult>
CreateOrderAsync(OrderDto orderDto)
{
orderDto.IdOrder =
await repository
.CreateOrderAsync(mapper.Map<Order>(orderDto));
return CreatedAtRoute(nameof(GetOrderByIdAsync),
new { idOrder = orderDto.IdOrder }, orderDto);
}
In my web application I have the class DataServiceHelper, which I use in the actual data services for the web app. Here I have methods for POST and GET and so on:
...
static public async Task PostAsync(HttpClient httpClient, string endpoint, T item)
{
await httpClient.PostAsJsonAsync(endpoint, item);
}
static public async Task<T> FindOneByIdAsync(HttpClient httpClient, string endpoint)
{
return await JsonSerializer.DeserializeAsync<T>
(await httpClient.GetStreamAsync(endpoint), new JsonSerializerOptions()
{ ropertyNameCaseInsensitive = true });
}
...
If I send a POST request to the API (say via Postman) the response contains (among others) the body, which is a jsonized version of my database object. Can I somehow catch that body in my Web App with JsonSerializer and return it like I do with my FindOneByIdAsync method?
Alternatively, in the API, could I create a new header in the responses headers that only contains the Id, that the database creates for the new object, and catch that in my web apps post method from the response?

Use System.Net.Http.Json. Examples:
Task<O> CallGet<O>(string requestUrl) => http.GetFromJsonAsync<O>(requestUrl);
async Task<O> CallPost<I, O>(string requestUrl, I input)
{
using var response = await http.PostAsJsonAsync(requestUrl, input);
return await response.Content.ReadFromJsonAsync<O>();
}
See the documentation for more methods and overloads.
It is available also as a nuget: https://www.nuget.org/packages/System.Net.Http.Json

Might be a little cumbersome, but this works:
static public async Task<T>PostAndListenAsync(HttpClient httpClient, string endpoint, T item)
{
var content = await httpClient.PostAsJsonAsync(endpoint, item);
var stream = await content.Content.ReadAsStreamAsync();
T t = await JsonSerializer.DeserializeAsync<T>(stream, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
return t;
}
Feel free to make a neat one-liner out of it.
My Data Service:
public async Task<OrderDto> PostOrderAsync(OrderDto orderDto)
{
return await DataServiceHelper<OrderDto>.PostAndListenAsync(httpClient, "/Orders", orderDto);
}
In razor:
...
private async Task CreateNewOrder()
{
OrderDto orderDto = await dataService.PostOrderAsync(newOrder);
}
...

Related

.NET API POST Endpoint not getting hit

I have a .net api and I want to test the api from a console app.
The method I am trying to test is a POST Method.I serialize data from my console app into a json string and I want to post it to the API, but the API does not get hit and I dont get any errors from my console app.
My GET calls work though. It is just the post I cant get to work.
My API Controller->
using _ErrorLogger.Shared;
using _ErrorLogger.Server.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Runtime.CompilerServices;
namespace _ErrorLogger.Server.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ExceptionDetailsController : ControllerBase
{
private readonly IExceptionDetailsService _exceptionDetailsService;
public ExceptionDetailsController(IExceptionDetailsService exceptionDetailsService)
{
_exceptionDetailsService = exceptionDetailsService;
}
[HttpGet]
[Route("GetExceptions")]
public async Task<List<ExceptionDetails>> GetAll()
{
return await _exceptionDetailsService.GetAllExceptionDetails();
}
[HttpGet]
[Route("GetExceptionByID/{id}")]
public async Task<ExceptionDetails> GetByID(int id)
{
return await _exceptionDetailsService.GetExceptionDetails(id);
}
[HttpPost]
[Route("CreateException")]
public async Task<IActionResult> CreateException([FromBody]string obj)
{
//await _exceptionDetailsService.AddExceptionDetails(exceptionDetails);
return Ok();
}
[HttpPost]
[Route("Test")]
public async Task<IActionResult> Test([FromBody] string obj)
{
return Ok();
}
}
}
My Call from the console app ->
public async void ExceptionsAnalyzer(Exception exception)
{
HttpClient _httpClient = new HttpClient();
StackTrace stack = new StackTrace(exception, true);
StackFrame frame = stack.GetFrame(stack.FrameCount - 1);
ExceptionDetails exceptionDetails = new ExceptionDetails
{
ExceptionMessage = exception.Message,
InnerException = exception.InnerException?.ToString(),
ExceptionType = exception.GetType().ToString(),
ExceptionSourceFile = frame.GetFileName(),
ExceptionSourceLine = frame.GetFileLineNumber().ToString(),
ExceptionCaller = frame.GetMethod().ToString(),
ExceptionStackTrace = exception.StackTrace,
DateLogged = DateTime.Now
};
string json = JsonSerializer.Serialize(exceptionDetails);
//var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = await _httpClient.PostAsJsonAsync("http://localhost:5296/api/ExceptionDetails/CreateException", json);
if (response.IsSuccessStatusCode)
{
}
}
I am Expecting the api endpoint to be hit.
I am Expecting the api endpoint to be hit.
Well, Firstly, your method in console app which is ExceptionsAnalyzer structure is wrong. It should be type of static because, main method within console app itself is type of static.
Another mistake is async should be type of Task and while calling the ExceptionsAnalyzer method it should be wait() for response but your console app is static so how it would handle await call? So see the solution below:
Solution:
using System.Net.Http.Json;
using System.Text.Json;
// Calling method
ExceptionsAnalyzer().Wait();
//Defining Method in dotnet 6 console app
static async Task ExceptionsAnalyzer()
{
HttpClient _httpClient = new HttpClient();
var obj = "Test data";
string json = JsonSerializer.Serialize(obj);
HttpResponseMessage response = await _httpClient.PostAsJsonAsync("http://localhost:5094/api/ExceptionDetails/CreateException", json);
if (response.IsSuccessStatusCode)
{
}
}
Note: I haven't consider your parameter Exception exception which you can modify yourself. I am mostly considering why you cannot get to hit API Endpoint. Hope you now got the mistake.
Output:
Unless ExceptionDetails is part of your basepath and as such is included for all API calls, I think you need to remove that.
You defined the route to the call as CreateException, so the url should be <base url>/CreateException
If that doesn't help, please post the code of your entire controller (with endpoint method).

.NET Core HttpClient upload byte array gives unsupported media type error

I'm trying to upload a simple byte array for my Web Api controller (ASP.NET Core 3)
using var client = new HttpClient() { BaseAddress = new Uri("http://someUrl.com/") };
var body = new ByteArrayContent(new byte[] {1, 2, 3});
var result = await client.PostAsync("api/somecontroller/content?someField=someData", body);
Controller
[HttpPost("content")]
public IActionResult Upload([FromBody]byte[] documentData, [FromQuery] string someField)
{
...
return Ok();
}
but this gives me the error 415 Unsupported media type. Why ? I need to put some additional data in the url, but I don't think that's the issue here.
While byte[] would be a great way to represent application/octet-stream data, this is not the case by default in asp.net core Web API.
Here is a simple workaround:
Send request by HttpClient:
using var client = new HttpClient() { BaseAddress = new Uri("http://localhost:62033") };
var body = new ByteArrayContent(new byte[] { 1, 2, 3 });
body.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
var result = await client.PostAsync("api/Values/content?someField=someData", body);
Receive action in Web Api project:
[HttpPost("content")]
public IActionResult Upload([FromBody]byte[] documentData, [FromQuery] string someField)
{
return Ok();
}
Custom InputFormatter in Web Api Project:
public class ByteArrayInputFormatter : InputFormatter
{
public ByteArrayInputFormatter()
{
SupportedMediaTypes.Add(Microsoft.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/octet-stream"));
}
protected override bool CanReadType(Type type)
{
return type == typeof(byte[]);
}
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
var stream = new MemoryStream();
await context.HttpContext.Request.Body.CopyToAsync(stream);
return InputFormatterResult.SuccessAsync(stream.ToArray());
}
}
Startup.cs in Web Api Project:
services.AddControllers(options=>
options.InputFormatters.Add(new ByteArrayInputFormatter()));
Result:
The issue is caused by the [FromBody] attribute which supports only simple types. You can use Type Converters for other types. The correct controller action code should be:
[HttpPost("content")]
public IActionResult content([FromQuery] string someField)
{
var documentData= new byte[Request.ContentLength.Value];
Request.Body.ReadAsync(documentData);
//...
return Ok();
}
Be aware that the answer above has typos, which may cost you quite a lot of time (code in the gif file is incorrect and may produce damaged not complete byte arrays):
public async override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
var stream = new MemoryStream();
await context.HttpContext.Request.Body.CopyToAsync(stream);
return InputFormatterResult.Success(stream.ToArray());
}
Based on the excellent answer from #Rena you can also use the following class to read the request body as a Stream parameter:
public class StreamInputFormatter: InputFormatter
{
public StreamInputFormatter()
=> SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/octet-stream"));
protected override bool CanReadType(Type ArgType) => ArgType == typeof(Stream);
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext ArgContext)
=> InputFormatterResult.SuccessAsync(ArgContext.HttpContext.Request.Body);
}
Your API controller action can then look something like this:
[HttpPost]
[Consumes("application/octet-stream")]
public async Task<ActionResult> UploadData([FromBody] Stream ArgData) {}
When you do the above and use Swagger UI via the Swashbuckle.AspNetCore package, it will correctly show a file-input control which allows you to upload the binary data:

Error while returning HttpResponseMessage in.NET core

I have a simple API gateway controller which returns an IActionResult. The issue is I am not able to read the body of the response.
If I comment out the using block in ExecuteResultAsync it seems to work fine but there is not content/body.
Not sure how to get this working with the httpbody being returned. RouteRequest returning HttpResponseMessage is not an option as it puts the response from the microservice as the body of the response from the Gateway.
So I need to use the HttpResponseMessageResult middleware, which works as expected for headers but not for the body.
public async Task<IActionResult> RouteRequest()
{
// Calls a method which send a request and gets a response and constructs a HttpResponseMessage
_contextAccessor.HttpContext.Response.RegisterForDispose(response);
return new HttpResponseMessageResult(response);
}
public class HttpResponseMessageResult : IActionResult
{
private readonly HttpResponseMessage _responseMessage;
public HttpResponseMessageResult(HttpResponseMessage responseMessage)
{
_responseMessage = responseMessage;
}
public async Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = (int)_responseMessage.StatusCode;
var responseMessageHeadersArray = _responseMessage.Headers.ToArray();
for (int i = 0; i < responseMessageHeadersArray.Length; i++)
{
var header = responseMessageHeadersArray[i];
context.HttpContext.Response.Headers.TryAdd(header.Key, new StringValues(header.Value.ToArray()));
}
using (var stream = await _responseMessage.Content.ReadAsStreamAsync())
{
await stream.CopyToAsync(context.HttpContext.Response.Body);
await context.HttpContext.Response.Body.FlushAsync();
}
}
}
Try this out, based on this good answer to a similar question, I used the ObjectResult class instead of manually manipulating the streams. When I run it with response from one of our API's (JSON), I get the same amount of data in the body of objectResult when it calls ExecuteAsync as were in the initial response.
public class HttpResponseMessageResult : IActionResult
{
private readonly HttpResponseMessage _responseMessage;
public HttpResponseMessageResult(HttpResponseMessage responseMessage)
{
_responseMessage = responseMessage;
}
public async Task ExecuteResultAsync(ActionContext context)
{
var objectResult = new ObjectResult(await _responseMessage.Content.ReadAsStreamAsync())
{StatusCode = (int)_responseMessage.StatusCode};
foreach (KeyValuePair<string, IEnumerable<string>> h in _responseMessage.Headers)
{
context.HttpContext.Response.Headers.TryAdd(h.Key, string.Join("", h.Value));
}
await objectResult.ExecuteResultAsync(context);
}
}

How to wrap graphql.net endpoint response using asp.net core 2 middleware?

I have REST API developed using asp.net web api2. I am migrating the REST API to GraphQL.net endpoints using asp.net core 2. In the existing REST API code I have a Delegating handler used to extend the result of REST API call with additional data which in this case is add localization data to the response.Since Delegating handler are no more supported in asp.net core 2. I am trying to migrate the existing Delegating handler to Middleware component.
For reference purpose I followed the details mentioned at : Extending WebApi response using OWIN Middleware and
https://www.devtrends.co.uk/blog/wrapping-asp.net-web-api-responses-for-consistency-and-to-provide-additional-information
Here I have couple of queries:
How to map the below code in case of Middlware ?
var response = await base.SendAsync(request, cancellationToken);
Where should I place the middleware in Startup.cs Configure method.
Middleware equivalent of the existing Delegating Handler
Code:
public class CommonResponserHandler : DelegatingHandler
{
ICommonService _commonService = new CommonService();
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
string locale = string.Empty;
if (request.Headers.Contains("Accept-Language"))
{
locale = request.Headers.GetValues("Accept-Language").First();
}
bool initialAuthorizationStatus = GetInitialAuthorization(request);
var response = await base.SendAsync(request, cancellationToken);
APIResult commonResponse;
if (response.TryGetContentValue<APIResult>(out commonResponse))
{
//populate common response here;
UpdateCommonResponse(request, response, commonResponse);
//UpdateCommonResponse(basicResponse, commonResponse);
HttpResponseMessage newResponse;
bool authorizatinCheckResult = AssertAuthorization(initialAuthorizationStatus, request);
if (authorizatinCheckResult)
{
newResponse = request.CreateResponse(response.StatusCode, commonResponse);
}
else
{
var unAuthorisedResult = new APIResult{Authorized = false, UserMessage = Constants.Unauthorized, Locale = new Locale(_commonService.GetLanguageFromLocale(locale))};
newResponse = request.CreateResponse(HttpStatusCode.Unauthorized, unAuthorisedResult);
var jsonSerializerSettings = new JsonSerializerSettings{ContractResolver = new CamelCasePropertyNamesContractResolver()};
HttpContext.Current.Items["401message"] = JsonConvert.SerializeObject(unAuthorisedResult, Formatting.Indented, jsonSerializerSettings);
}
//Add headers from old response to new response
foreach (var header in response.Headers)
{
newResponse.Headers.Add(header.Key, header.Value);
}
return newResponse;
}
return response;
}
}
Can anyone help me to provide their guidance in resolving the issue?
Please read the ASP.NET Core Middleware documentation for a better understanding on how middlewares work.
The middleware takes in the next RequestDelegate in its constructor and supports an Invoke method .For example :
public class CommonResponserMiddleware
{
private readonly RequestDelegate _next;
public CommonResponserMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
//process context.Request
await _next.Invoke(context);
//process context.Response
}
}
public static class CommonResponserExtensions
{
public static IApplicationBuilder UseCommonResponser(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CommonResponserMiddleware>();
}
}
And use in Starup.cs:
public void Configure(IApplicationBuilder app) {
//...other configuration
app.UseCommonResponser();
//...other configuration
}
You can also refer to related SO question:
Registering a new DelegatingHandler in ASP.NET Core Web API
How can I wrap Web API responses(in .net core) for consistency?

Multiple operations with path 'api/External' and method 'GET' while calling an external Web API

I am trying to call an externally hosted Web API within my Web API Application.
While attempting to use Get () method, everything seems to work fine.
However, when I try to implement Get(int value) I am getting an error:
Multiple operations with path 'api/External' and method 'GET'.
What is wrong with my Controller?
public class ExternalController : ApiController
{
static string _address = "http://localhost:00000/api/Values";
private string result;
// GET api/values
public async Task<IEnumerable<string>> Get()
{
var result = await GetExternalResponse();
return new string[] { result, "value2" };
}
private async Task<string> GetExternalResponse()
{
var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(_address);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
return result;
}
public async Task<string> Get(int value)
{
var result = await GetExternalResponse();
return result;
}
}
I have also tried the below approach, which also seems to throw the same error:
private async Task<string> GetExternalResponse2(int value)
{
var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(_address + "/" + value);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
return result;
}
public async Task<string> Get(int value)
{
var result = await GetExternalResponse2(value);
return result;
}
When you do your get like this
Get api/External/{id}
web api is not sure whether to call the Get Method with no parameter or the Get Method with Parameter because of the default routing defined in your web api config
I would suggest using attribute routing to fix your problem
[Route("api/External/Get")]
public async Task<IEnumerable<string>> Get()
[Route("api/External/Get/{id}")]
public async Task<string> Get(int value)

Categories