How to add header in IHttpActionResult in Web API 2? - c#

Hi i am developing API's using Web API 2. I know how to add header when using HttpResponseMessage. Now I am using IHttpActionResult.
Below is my sample current code.
return Content(HttpStatusCode.OK, LoginResponse);
How can I add a header when I am returning content?
Whenever I use HttpResponseMessage I will be having request object and I can add header.
Below code I tried in HttpResponseMessage.
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response.Headers.AddCookies(new[] { cookie });
return response;
In this case where can I add header values?

You can continue to use the HttpResponseMessage as you are accustom to and update the header. After which you can use the IHttpActionResult ResponseMessage(HttpResponseMessage) method to convert to IHttpActionResult
Simple example
public class MyApiController : ApiController {
public IHttpActionResult MyExampleAction() {
var LoginResponse = new object();//Replace with your model
var cookie = new CookieHeaderValue("name", "value");//Replace with your cookie
//Create response as usual
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK, LoginResponse);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response.Headers.AddCookies(new[] { cookie });
//Use ResponseMessage to convert it to IHttpActionResult
return ResponseMessage(response);
}
}

You can create a custom IHttpActionResult which decorates a real one but exposes a way to manipulate the response:
public class CustomResult : IHttpActionResult
{
private readonly IHttpActionResult _decorated;
private readonly Action<HttpResponseMessage> _response;
public CustomResult(IHttpActionResult decorated, Action<HttpResponseMessage> response)
{
_decorated = decorated;
_response = response;
}
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = await _decorated.ExecuteAsync(cancellationToken);
_response(response);
return response;
}
}
Then use this in your action:
return new CustomResult(Content(HttpStatusCode.OK, loginResponse), res => res.Headers.AddCookies(new []{ cookie}));

You can add header by using this code:
HttpContext.Current.Response.AppendHeader("Some-Header", value);
or this
response.Headers.Add("Some-Header", value);

Related

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 return an html document from WebApi 2 action using IHttpActionResult interface?

I need to build an html document and return it in my web api. All available answers on the net and the forum suggest using HttpResponseMessage. I would like to achieve this by IHttpActionResult. Below is what I have thus far:
[ResponseType(typeof(HttpResponseMessage))]
public async Task<IHttpActionResult> GetNotesViewModels()
{
var note = await GetHtmlText();
var response = new HttpResponseMessage();
response.Content = new StringContent(note);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return Ok(ResponseMessage(response));
}
I am not receiving what I would like. What is missing here?
You could implement your own HtmlResult, like following (free-handed):
public class HtmlActionResult : IHttpActionResult
{
public HtmlActionResult (HttpRequestMessage request, string content)
{
Request = request;
Content= content;
}
public string Content { get; private set; }
public HttpRequestMessage Request { get; private set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(ExecuteResult());
}
public HttpResponseMessage ExecuteResult()
{
var response = new HttpResponseMessage();
if (!string.IsNullOrWhiteSpace(Content))
response.Content = new StringContent(Content, Encoding.UTF8, "text/html");
response.RequestMessage = Request;
return response;
}
}
And use it like this:
public async Task<IHttpActionResult> GetNotesViewModels()
{
var note = await GetHtmlText();
return new HtmlActionResult(Request, note);
}

How to receive a file in ASP.NET Core controller

I want to send an image with C# HttpClient, receive it in ASP.NET Core controller and save it to disk. I tried various methods but all i'm getting in controller is null reference.
My http client:
public class HttpClientAdapter
{
private readonly HttpClient _client;
public HttpClientAdapter()
{
_client = new HttpClient();
}
public async Task<HttpResponse> PostFileAsync(string url, string filePath)
{
var requestContent = ConstructRequestContent(filePath);
var response = await _client.PostAsync(url, requestContent);
var responseBody = await response.Content.ReadAsStringAsync();
return new HttpResponse
{
StatusCode = response.StatusCode,
Body = JsonConvert.DeserializeObject<JObject>(responseBody)
};
}
private MultipartFormDataContent ConstructRequestContent(string filePath)
{
var content = new MultipartFormDataContent();
var fileStream = File.OpenRead(filePath);
var streamContent = new StreamContent(fileStream);
var imageContent = new ByteArrayContent(streamContent.ReadAsByteArrayAsync().Result);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
content.Add(imageContent, "image", Path.GetFileName(filePath));
return content;
}
}
and controller:
[Route("api/files")]
public class FilesController: Controller
{
private readonly ILogger<FilesController> _logger;
public FilesController(ILogger<FilesController> logger)
{
_logger = logger;
}
[HttpPost]
public IActionResult Post(IFormFile file)
{
_logger.LogInformation(file.ToString());
return Ok();
}
}
As i mentioned above, the IFormFile object i'm getting in the controller is null reference. I tried adding [FromBody], [FromForm], tried creating class with two properties: one of type string and one with type IFormFile, but nothing works. Also instead of sending file with C# HttpClient i used Postman - same thing happens.
Does anyone know solution for this problem? Thanks in advance.
The name of the form field must match the property name:
content.Add(imageContent, "file", Path.GetFileName(filePath));
file instead of image, since you use file in
public IActionResult Post(IFormFile file)
{
}

How can I read JSON from a StringContent object in an ApiController?

I'm writing an API controller intended to receive and parse the contents of a JSON asynchronous post, and am unable to read the contents of the StringContent object in that post.
Here is the section from my API controller where I expect to see the value. The value arriving in the ApiController method is null. And the jsonContent value is an empty string. What I'm expecting to see is the contents of a JSON object.
public class ValuesController : ApiController
{
// POST api/values
public void Post([FromBody]string value)
{
HttpContent requestContent = Request.Content;
string jsonContent = requestContent.ReadAsStringAsync().Result;
// Also tried this per mybirthname's suggestion.
// But content ends up equaling 0 after this runs.
var content = Request.Content.ReadAsStreamAsync().Result.Seek(0, System.IO.SeekOrigin.Begin);
}
}
here is my controller to show how it's being called.
[HttpPost]
public ActionResult ClientJsonPoster(MyComplexObject myObject)
{
this.ResponseInfo = new ResponseInfoModel();
PostToAPI(myObject, "http://localhost:60146", "api/values").Wait();
return View(this.ResponseInfo);
}
And this is the posting method.
private async Task PostToAPI(object myObject, string endpointUri, string endpointDirectory)
{
string myObjectAsJSON = System.Web.Helpers.Json.Encode(myObject);
StringContent stringContent = new StringContent(myObjectAsJSON, Encoding.UTF8, "application/json");
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(endpointUri);
using (HttpResponseMessage responseMessage = await httpClient.PostAsJsonAsync(endpointDirectory, stringContent).ConfigureAwait(false))
{
// Do something
}
}
}
I suspect something is wrong with the signature of the Post method inside the ApiController. But don't know how that should be changed. Thanks for your help.
You are mixing async and sync calls which will lead to deadlocks.
Update controller to
[HttpPost]
public async Task<ActionResult> ClientJsonPoster(MyComplexObject myObject) {
this.ResponseInfo = new ResponseInfoModel();
await PostToAPI(myObject, "http://localhost:60146", "api/values");
return View(this.ResponseInfo);
}
Also [FromBody] is used to force Web API to read a simple type from the request body.
Update Api
public class ValuesController : ApiController {
// POST api/values
[HttpPost]
public async Task Post() {
var requestContent = Request.Content;
var jsonContent = await requestContent.ReadAsStringAsync();
}
}

Creating cookie when using odata and web api 2

How do I set a cookie when using Web api 2 and odata. I am new to this api and traditionally I used the context.Response but it does not seem to be avaliable here.
This is a part of my controller code:
public async Task<IHttpActionResult> Post(Order Order)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
context.Orders.Add(Order);
await context.SaveChangesAsync();
return Created(Order);
}
If you are using the IHttpActionResult class there's a function within it Task<System.Net.Http.HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken);
You can use that function to return the HttpResponseMessage and add cookies to the response message.
I would make another class that implements IHttpActionResult similar to this:
public class OrderResult : IHttpActionResult
{
Order _order;
HttpRequestMessage _request;
public OrderResult(Order order, HttpRequestMessage request)
{
_order = value;
_request = request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage()
{
Content = new StringContent(_value),
RequestMessage = _request
};
var cookie = new CookieHeaderValue("session-id", "6789");
cookie.Expires = DateTimeOffset.Now.AddDays(1);
cookie.Domain = Request.RequestUri.Host;
response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
return Task.FromResult(response);
}
}
You will need to adjust your controller code to call this new class. e.g.
public async Task<IHttpActionResult> Post(Order Order)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
context.Orders.Add(Order);
await context.SaveChangesAsync();
return new OrderResult(Order, request /* not sure how you'll get the request in this scope*/);
}
You can write your own DelegatingHandler to add the cookie you need into the response.
Check the part "Example: Set and Retrieve Cookies in a Message Handler":
http://www.asp.net/web-api/overview/working-with-http/http-cookies
For how to insert a message handler, check this:"Per-Route Message Handlers"
http://www.asp.net/web-api/overview/working-with-http/http-message-handlers

Categories