I am having trouble reading the Location header from parts of a multipart response of an http request. I am using HttpClient, and processing the resultant HttpResponseMessage (mostly) as follows:
protected internal virtual async Task ProcessMultiPartResponseAsync(HttpResponseMessage response)
{
var multipart = await response.Content.ReadAsMultipartAsync();
await Task.WhenAll(multipart.Contents.Select(ProccessContentAsync));
}
protected async Task ProccessContentAsync(HttpContent content)
{
var location = content.Headers.GetValues("Location").FirstOrDefault(); //nada
//...
}
I have verified the Location header is actually in the response using fiddler:
And that the other headers are being read successfully:
From inspecting the non-public property headers.invalidHeaders I see Location listed, as shown below, which seems suspect to me...
Is that my problem? If so how can I get around it. If not what am I doing wrong? Thank you so much for any help.
Related
There is exception being occurred at client side saying Error while copying the stream content and below that the response ended prematurely.
I cant figure out the solution. In the server side I have asp.net core web api which modify response stream. It actually reads what the controller send and encrypts it to a string and then writes to the response stream.
Also when when the content type is text/plain the response is shown on the Postman but when the content-type is application/json the content is not shown but in the header I can see the content length having some numbers. And for client side, the above exception occurs for both the content-type.
So what i am missing in my middleware code?? I know this is causing issue because when i comment out MyMiddleware in startup.cs, the normal flow works.
Below is the Invoke function in middleware on the server side
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
string reponseContent = string.Empty;
// Store the "pre-modified" response stream.
var existingBody = context.Response.Body;
using (var newBody = new MemoryStream())
{
// We set the response body to our stream so we can read after the chain of middlewares have been called.
context.Response.Body = newBody;
await next(context);
// Set the stream back to the original.
context.Response.Body = existingBody;
newBody.Seek(0, SeekOrigin.Begin);
//reading the content
var contentReader = new StreamReader(newBody);
reponseContent = await contentReader.ReadToEndAsync();
string encryptedData = _cryptoService.Encrypt(reponseContent);
// Send our modified content to the response body.
await context.Response.WriteAsync(encryptedData);
}
I am not sure how you are calling your MiddleWare. I have successfully reproduce the issue and get the response accordingly.
Middle Ware Class:
public class CustomMiddleware
{
private readonly RequestDelegate next;
public CustomMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
string reponseContent = string.Empty;
// Store the "pre-modified" response stream.
var existingBody = context.Response.Body;
using (var newBody = new MemoryStream())
{
// We set the response body to our stream so we can read after the chain of middlewares have been called.
context.Response.Body = newBody;
await next(context);
// Set the stream back to the original.
context.Response.Body = existingBody;
newBody.Seek(0, SeekOrigin.Begin);
//reading the content
var contentReader = new StreamReader(newBody);
reponseContent = await contentReader.ReadToEndAsync();
// string encryptedData = _cryptoService.Encrypt(reponseContent);
// Send our modified content to the response body.
await context.Response.WriteAsync(reponseContent);
}
}
}
Note: You should use constructor to invoke your RequestDelegate like this way. But you have designed this with two parameter, not sure how you are passing the argument while calling.
Startup.cs:
Calling Middleware In Startup.cs under Configure like this way
app.UseMiddleware<CustomMiddleware>();
Request From Postman:
I have tested with simple plain text and application/json type. Sent request to my controller and modify the argument on controller body, and the changed implemented on the middleware.
Controller:
[HttpPost]
public IActionResult MiddlewareReStream([FromBody] Plans plan)
{
plan.PlanName = "New Text";
return Ok(plan);
}
MiddlWare Output:
Note: Notice that I have invoke the request with "PlanName":"Test Plan" and modified the parameter which middleware successfully invoked the changes.
PostMan:
Note: Make sure you have called or implemented the InvokeAsync Middleware accordingly. Because I got the response as you are expecting.
Im thinking that the "Content-Length" header of the response represents the size of the data before you encrypt it and that you need to recalculate the size based on the new encrypted data and reset the header. Im also thinking that different servers are reacting to this missmatch differently, That it might work on IIS but not Kestrel.
In short make sure your "Content-Length" and "Content-Type" headers are matching what you are actually sending.
I have an Azure Function that sits behind a proxy. If an update occurs to the objects that get returned we want to deprecate the Function after a period of time. I'm trying to create a response with the expected content from an HTTP Header by using what was provided in this solution.
Warning: 299 - "Deprecated API"
I try to append the Azure Function like so:
[FunctionName("MyAPI")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function,
"post", Route = null)]
HttpRequestMessage req,
TraceWriter log)
{
object response = await someService.Get();
if (settingsService.IsDeprecated)
{
var httpresponse = req.CreateResponse(HttpStatusCode.OK, response, "application/json");
httpresponse.Content.Headers.Add("Warning", "299 - Deprecated API");
return httpresponse;
}
return req.CreateResponse(HttpStatusCode.OK, response, "application/json");
}
I get the Exception
Exception while executing function: MyAPI -> Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.
How do I append the "Deprecated" status warning in my API Http Response?
Change your line to
httpresponse.Headers.Add("Warning", "299 - \"Deprecated API\"");
The quotes seem to be important there to adhere to the format requirement.
I'm trying to obtain a published data from a ASP.NET file who publish all the data related to certain string i send in POST format.
The web-service is working fine and i already do the necesary test to see if is working properly.
Is valid mention than before the web-server was publishing all the data just fine, but i now want to add this aditional filter layer, to optimize the readings of the android app when reading the publication.
Here is the code of the method i use to get the data (who used to work just fine) but now i'm also using it to send a string to obtain certain specific data, and doesn't seems to work:
namespace AndroidApp.REST
{
public static class Client
{
public async static Task<T> GetRequest<T>(this string url)
{
try
{
HttpClient client = new HttpClient();
//Preparing to have something to read
var stringContent = new StringContent("someHardCodedStringToTellTheServerToPublishTheDataTheAppWillConsume");
var sending = await client.PostAsync(url, stringContent);
//Reading data
var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
}
catch
{
return default(T);
}
}
}
}
Any question, comment, request for clarification or anything than helps to obtain an answer or improve the clarity of the question would be much apreciated too
Thanks in advance
In the latest Web API 2, how do I configure it so that it will only return a reply if the Accept header is application/json? This API will only support json, if any other accept header is sent then an error must be thrown. There will be no xml and even no html interface.
If the client asks for xml or html or anything, we need to throw an error to let them know they used the wrong accept type. We must not mask this problem by replying with the correct json when they have requested a type that is not actually supported.
var request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/json";
var response = request.GetResponse();
And the json result is returned successfully. But if there is any other Accept then an error is returned
var request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/xml"; // or text/html or text/plain or anything
var response = request.GetResponse();
Returns HTTP 501 Not Implemented or similar http error code.
This question is not a duplicate of How do I get ASP.NET Web API to return JSON instead of XML using Chrome? - that question asks how to also return json. My question is how to only return json, and only if the client asks for json. If the client asks for any other type like xml or html, then an error is returned.
This page shows how to access content negotiation directly. You could conceivably instead pass some filtered subset of this.Configuration.Formatters containing only the desired formatters to IContentNegotiator.negotiate, like so:
ContentNegotiationResult result = negotiator.Negotiate(
typeof(Product), this.Request, this.Configuration.Formatters.Where(/* some expression that excludes all but the json formatter */);
This looks quite clumsy and would be a lot of dull boilerplate, so Javad_Amiry's answer is probably better, but this is another option that might be useful in specific cases.
You can clear all formatters except JSON:
configuration.Formatters.Clear();
configuration.Formatters.Add(new JsonMediaTypeFormatter());
Or you can change the default Web API’s content negotiation mechanism:
public class JsonContentNegotiator : IContentNegotiator
{
private readonly JsonMediaTypeFormatter _jsonFormatter;
public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
{
_jsonFormatter = formatter;
}
public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
var result = new ContentNegotiationResult(_jsonFormatter, new MediaTypeHeaderValue("application/json"));
return result;
}
}
// in app_start:
var jsonFormatter = new JsonMediaTypeFormatter();
config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
See the article.
UPDATE:
Well, if you want to return a HTTP error on non-json request, you can do it by implementing a custom IHttpModule for checking header. But, for self-host apps it won't work. So, it's better to use extend a custom DelegatingHandler. For example, you can use this one:
public class FilterJsonHeaderHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken){
if (request.Headers.Accept.All(a => a.MediaType == "application/json")){
// if we have only application/json, so the pipeline continues
return base.SendAsync(request, cancellationToken);
}
// otherwise, do whatever you want:
var response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
var completionSource = new TaskCompletionSource<HttpResponseMessage>();
completionSource.SetResult(response);
return completionSource.Task;
}
}
and register it in app_start:
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.MessageHandlers.Add(new FilterJsonHeaderHandler());
// your other settings...
}
}
NOTE: the code is not tested. Please let me know if there is any error.
Quite a simple problem I've got that would be easy to solve I reckon for the brains. It's just a tried a different queries on Google and nothing popped up but hey that's why I am here.
Here is the error:
System.InvalidOperationException
Basically this error is thrown on this piece of code here
string test = response.Content.Headers.GetValues("Location").FirstOrDefault();
Location in Fiddler looks like this:
Location: https://www.redirecturlishere.com/blah
Here is the entire function:
private async Task<string> GetRequest()
{
//HttpContent postContent = new StringContent(post, Encoding.ASCII, "application/x-www-form-urlencoded");
using (HttpResponseMessage response = await httpClient.GetAsync(
"http://www.cant-share-url.com"))
{
using (HttpContent content = response.Content)
{
string test = response.Content.Headers.GetValues("Location").FirstOrDefault();
}
}
return "";
}
More details on the error, "Additional information: Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."
I don't think there is much else to explain so I'll leave it at that.
I have solved the problem basically the header was not stored in the content, silly me.
And for anyone who doesn't see the solution. :)
string test = response.Headers.GetValues("Location").FirstOrDefault()