Using the WCF Web API how would I go about changing a response's content body after the application logic has been run but before it's returned to the user. The goal is if suppressstatuscontent is true we:
Add a statuscode field to the content body
Change the statuscode on the response to 200
I have overridden a DelegatingChannel and in the SendAsnyc have some code that looks like this:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>(task =>
{
var response = task.Result;
if (CheckIfRequestHadSuppressStatusCode(request) == true)
{
string newResponse = (response.Content == null) ? "" : response.Content.ReadAsString();
newResponse = "<body>" +newResponse + "</body><statuscode>" + response.StatusCode + "</statuscode>";
response.StatusCode = HttpStatusCode.OK;
}
return response;
});
A major problem is this doesn't handle BOTH, xml and Json. I feel like there must be a much better way to go about the problem as this feels very hacky.
I'm not sure of the right approach but I would try something like this:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith<HttpResponseMessage>(task =>
{
var response = task.Result;
if (CheckIfRequestHadSuppressStatusCode(request) == true)
{
switch(response.Content.Headers.ContentType.MediaType)
{
case "application/xml":
response.Content = new XmlWithStatusContent(response.Content)
break;
case "application/json":
response.Content = new JsonWithStatusContent(response.Content)
break;
}
response.StatusCode = HttpStatusCode.OK;
}
return response;
});
}
You can encapsulate the code that adds the extra status code markup in specialized versions of HttpContent (e.g. XmlWithStatusContent and JsonWithStatusContent).
You could parse the content as either XML or JSON (you can encapsulate that functionality in it's own class) which then gives you the ability to add the field independent of the format (independent in the sense that the SendAsync doesn't need to know).
var newResponse = ContentDecoder.Parse(response.Content).AddField("statuscode", response.StatusCode).ToString();
assuming Parse would return come kind of content object you can modify without having to know what the format is.
It's not a really a nice solution but it encapsulates the hackiness away a bit.
Update: Assuming you can dereive from HttpResponseMessage and are in full control of generating the response then you could have specialised subclasses dealing with it:
interface IHttpResponseContent
{
void AddField(string name, string value);
}
class XmlHttpResponseMessage : HttpResponseMessage, IHttpResponseContent
{
}
When generating the response you create Xml/JsonHttpResponseMessage objects instead. Then you can do:
var newResponse = response as IHttpResponseContent;
if (newResponse != null)
{
newResponse.AddField("statuscode", response.StatusCode);
}
Related
Good day,
I am currently using ASP.NET Core to make request to some service endpoints with strict Header rules.
I discovered that RequestId is added automatically to my request header, i need to remove this.
Here is my code snippet;
client.BaseAddress = new Uri(mainUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", authorization);
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
var responseMessage = await client.PostAsJsonAsync(mainUrl, model);
var errorMessage = responseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
StreamReader sr = new StreamReader(await responseMessage.Content.ReadAsStreamAsync());
resps = sr.ReadToEnd();
I also tried using flur but the situation remains thesame. Below is the snippet of the code;
var resps = await url.AppendPathSegment("/merchant/api/paymentinit")
.WithHeader("Authorization", authorization)
.PostJsonAsync(model)
.ReceiveString();
I have also tried following suggestion from enter link description here
But nothing seems to work. Can someone put me in the right direction?
You'll need to create a DelegatingHandler and set Activity.Current = null
public class DisableActivityHandler : DelegatingHandler
{
public DisableActivityHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Activity.Current = null;
return await base.SendAsync(request, cancellationToken);
}
}
To read more about this issue see David Fowlers comments here
https://github.com/dotnet/aspnetcore/issues/19044
I have a web api which returns IActionResult.
I return FileContentResult from this api like this
return new FileContentResult(model.Content, ContentType)
{
EnableRangeProcessing = true
};
I have a requirement in which I now want to control StatusCode myself, rather than FileContentResult decide itself.
I don't find any way to do this.
Basically I want to return my own designed HttpResponseMessage in which I can set headers and other stuff myself.
But I don't find any way to do this for IActionResult type.
The only thing that I thought could work is to use ResponseMessageResult something like this
var content = new ByteArrayContent(bytesWithValidationData);
var response = new HttpResponseMessage();
response.Content = content;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.StatusCode = HttpStatusCode.PartialContent;
response.Content.Headers.ContentRange = new ContentRangeHeaderValue(from, to);
return new ResponseMessageResult(response);
But its response is not same as HttpResponse, it just returns json result with HttpResponseMessage object details but does not actually return Http response considering content type etc. where I can download the file.
It gives result like this
Is there any way I can return my designed file result type http response?
Legacy ASP.NET Core web API had special handling for raw HttpResponseMessage instances. ASP.NET Core does not - your controller action has to return an instance of IActionResult.
In your case, I would suggest subclassing FileContentResult and manipulating the status code, then returning your subclass from your controller. Something like the following:
public class MyFileContentResult : FileContentResult
{
public override Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = <your status code>;
var result = base.ExecuteResultAsync(context);
return result;
}
}
I am making a POST request to a route which is returning JSON data.
[HttpPost("api/v1/testGetAll")]
public object Test([FromBody]object filteringOptions)
{
return myService.GetLogs(filteringOptions).ToArray();
}
Route works fine, filtering works fine, and when I test the route in Postman I get the right response. However this is only a back-end, and I would like to invoke this route from my custom API gateway.
The issue I'm facing is getting that exact response back. Instead I am getting success status, headers, version, request message etc.
public object TestGetAll(string ApiRoute, T json)
{
Task<HttpResponseMessage> response;
var url = ApiHome + ApiRoute;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
try
{
response = client.PostAsync(url, new StringContent(json.ToString(), Encoding.UTF8, "application/json"));
return response.Result;
}
catch (Exception e)
{
...
}
}
}
How can I get exact content back?
You need to read the content from response.
var contentString = response.Result.Content.ReadAsStringAsync().Result;
If you wish, you can then deserialize the string response into the object you want returning.
public async Task<TResult> TestGetAll<TResult>(string apiRoute, string json)
{
// For simplicity I've left out the using, but assume it in your code.
var response = await client.PostAsJsonAsync(url, json);
var resultString = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<TResult>(resultString);
return result;
}
You have to return the response as an HttpResponseMessage.
Try changing your return statement to
[HttpPost("api/v1/testGetAll")]
public IHttpActionResult Test([FromBody]object filteringOptions)
{
return Ok(myService.GetLogs(filteringOptions).ToArray());
}
Please note: This will return the response with status code 200. In case you want to handle the response based on different response code. You can create the HttpResponseMessage like this-
Request.CreateResponse<T>(HttpStatusCode.OK, someObject); //success, code- 200
Request.CreateResponse<T>(HttpStatusCode.NotFound, someObject); //error, code- 404
T is your object type.
And so on...
I've created a custom MessageHandler as such:
public class WebAPICustomMessageHandler : DelegatingHandler {
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
//Parse QueryString
NameValueCollection queryString = HttpUtility.ParseQueryString(request.RequestUri.Query);
if (queryString != null) {
//Find my token
String token = queryString.Get("qsVariable");
if (!String.IsNullOrWhiteSpace(token)) {
//Remove token so it doesn't impact future handlers / controllers / etc.
queryString.Remove("qsVariable");
request.RequestUri.Query = queryString.ToString(); //How can we modify the querystring? apparently it's readonly?
//Append token as custom header to the request
request.Headers.Add("token", new String[] { token });
}
}
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
return response;
}
}
It appears that I can't directly change the QueryString, which is a bit odd as I thought that the entire point of custom message handlers in the Web API pipeline was to allow this exact sort of thing. The ability issue pre and post operations on request/response, including things like data scrubbing / injection etc.
I already know I can do what I want very easily utilizing OWIN (as I've already done it), but now I'm trying to do this without OWIN. Am I going to have to create an entirely new HttpRequestMessage in order to just change the QueryString? From the looks of it, I'll have to build a new Uri, then construct the HttpRequestMessage, then copy over each and every other piece from the original.
Is that the only way to do this? or is there a better way that I'm just not aware of?
Please note, that other routines later in the pipeline are setup to use a token found in the header of the request, but setting the header is not possible if the request came from a submission to an iframe, which is where the process above comes into place. The token is added to the querystring, then converted to a header to prevent changes to the rest of the pipeline.
You're correct, the query string is read-only. Just use a standard redirect with the substituted query string values.
public class WebAPICustomMessageHandler : DelegatingHandler {
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
var token = query["qsVariable"];
// just 'short-circuit' if our token was not found...
if (token == null)
return await base.SendAsync(request, cancellationToken);
token = "newValue"; // modify your value here...
query["token"] = token;
// redirect with new query string...
var response = request.CreateResponse(HttpStatusCode.Redirect);
var uri = request.RequestUri;
var ub = new UriBuilder(uri.Scheme,
uri.Host,
uri.Port,
uri.AbsolutePath);
ub.Query = query.ToString();
response.Headers.Location = ub.Uri;
return response;
}
}
For interest sake, it is possible to modify the query string even though it is read-only but I would avoid this practice.
How can I read the contents on the PUT request in MVC webApi controller action.
[HttpPut]
public HttpResponseMessage Put(int accountId, Contact contact)
{
var httpContent = Request.Content;
var asyncContent = httpContent.ReadAsStringAsync().Result;
...
I get empty string here :(
What I need to do is: figure out "what properties" were modified/sent in the initial request (meaning that if the Contact object has 10 properties, and I want to update only 2 of them, I send and object with only two properties, something like this:
{
"FirstName": null,
"LastName": null,
"id": 21
}
The expected end result is
List<string> modified_properties = {"FirstName", "LastName"}
By design the body content in ASP.NET Web API is treated as forward-only stream that can be read only once.
The first read in your case is being done when Web API is binding your model, after that the Request.Content will not return anything.
You can remove the contact from your action parameters, get the content and deserialize it manually into object (for example with Json.NET):
[HttpPut]
public HttpResponseMessage Put(int accountId)
{
HttpContent requestContent = Request.Content;
string jsonContent = requestContent.ReadAsStringAsync().Result;
CONTACT contact = JsonConvert.DeserializeObject<CONTACT>(jsonContent);
...
}
That should do the trick (assuming that accountId is URL parameter so it will not be treated as content read).
You can keep your CONTACT parameter with the following approach:
using (var stream = new MemoryStream())
{
var context = (HttpContextBase)Request.Properties["MS_HttpContext"];
context.Request.InputStream.Seek(0, SeekOrigin.Begin);
context.Request.InputStream.CopyTo(stream);
string requestBody = Encoding.UTF8.GetString(stream.ToArray());
}
Returned for me the json representation of my parameter object, so I could use it for exception handling and logging.
Found as accepted answer here
Even though this solution might seem obvious, I just wanted to post it here so the next guy will google it faster.
If you still want to have the model as a parameter in the method, you can create a DelegatingHandler to buffer the content.
internal sealed class BufferizingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
await request.Content.LoadIntoBufferAsync();
var result = await base.SendAsync(request, cancellationToken);
return result;
}
}
And add it to the global message handlers:
configuration.MessageHandlers.Add(new BufferizingHandler());
This solution is based on the answer by Darrel Miller.
This way all the requests will be buffered.