Response pipeline - asp.net-core

I came across a difficulty while was working with Asp.net core 1.0 RTM. For example in case bellow we will see output result as "-Message_1--Message_5-":
public class MessageMiddleware
{
private readonly RequestDelegate _next;
private readonly IApplicationBuilder _app;
public MessageMiddleware(RequestDelegate next, IApplicationBuilder app)
{
_next = next;
_app = app;
}
public async Task Invoke(HttpContext context)
{
var started1 = context.Response.HasStarted;//false
await context.Response.WriteAsync("-Message_1-");
var test = true; // will hit this line
var started2 = context.Response.HasStarted;//true
await context.Response.WriteAsync("-Message_5-");
await _next.Invoke(context);
}
}
But in this case (header "Content-Type" was added) the result will be only "-Message_1-" and execution is really stopped:
public class MessageMiddleware
{
private readonly RequestDelegate _next;
private readonly IApplicationBuilder _app;
public MessageMiddleware(RequestDelegate next, IApplicationBuilder app)
{
_next = next;
_app = app;
}
public async Task Invoke(HttpContext context)
{
var started1 = context.Response.HasStarted;//false
await context.Response.WriteAsync("-Message_1-");
var started2 = context.Response.HasStarted;//true
context.Response.ContentType = "text/html";
var test = true; // will NOT hit this line
var started3 = context.Response.HasStarted;//will NOT hit this line
await context.Response.WriteAsync("-Message_5-"); //will NOT hit this line
await _next.Invoke(context);
}
}
I found only this remark in official documentation:
Avoid modifying HttpResponse after invoking next, one of the next components in the pipeline may have written to the response, causing it to be sent to the client.
and this question at SO: Why can't the HttpResponse be changed after 'next' call?
But it's not enough to understand interaction with props of HttpContext.Response during middleware pipeline and how this interection affects on final result - headers and body content of HttpResponse.
Could somebody explain general behaviour of processing response by ASP.NET core? For example, when response headers are send to client and how setting HttpContext.Response properties(headers, body content) affects on this?
When pipeline inside(outside) middliware is terminated?
Thank you!

As a general rule, when the client makes a request to the server, it gets back a response. That response contains headers and a body. The headers contain many pieces of information about the response like the content type, encoding/compression used, cookies, etc. Here is an example of the headers sent back by the live.asp.net site as seen in the chrome developer tools:
The other part of the response is the body. It often contains html or json. Here is a screenshot of the body for the same response:
The easiest way to think about it is to think of these two being sent together to the client, first the headers then the body. So as a developer, your only opportunity to set any value on the response object that affects the headers is up to the point at which you start sending the body. One you begin sending the body of the response you can no longer change the headers because they are sent as the first part of the response just before the body begins sending.
That's why #tseng said "Don't set headers after you have written something to the response stream".
If a developer isn't familiar with http headers they might not realize that context.Response.ContentType = "text/html" is changing a header, but under the hood, that's exactly what it is doing. Likewise, setting a cookie changes a response header under the hood. In general, if you are changing some property of the response object you should ask yourself "will this change an http header?" and if the answer is "yes" then you need to do it before you make a call to Response.WriteAsync.

Related

Set value configuration.GetSection("").Value from header request

I need to set in my asp.net core configuration a value from the header in every request.
I'm doing like so:
public async Task Invoke(HttpContext context)
{
var companyId = context.Request.Headers["companyid"].ToString().ToUpper();
configuration.GetSection("CompanyId").Value = companyId;
await next(context);
}
It works fine. But is this the proper way? In case of multiple request at same time is there a risk of messing the values? I've searched around but couldn't find an answer.
I'm using .Net 3.1.
As far as I know, the appsetting.json value is a global setting value, you shouldn't be modifying global state per request, this action is not thread safe. At some point, you will face a rice condition.
If you still want to use this codes, I suggest you could try to add a lock. Notice: This will make your Invoke method very slowly.
Details, you could refer to below codes:
private static Object _factLock = new Object();
lock (_factLock)
{
Configuration.GetSection("CompanyId").Value = "";
}

Adding a WEB API method ruins my SWAGGER UI

This first method is fine. But when I add the second method the body of the SWAGGER UI is a bunch of html gibberish. And I creating the route the wrong way?
// GET api/checklist/1288
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
var model = _checkListService.Get(id);
return Ok(model);
}
// http://localhost:64783/api/checklist/GetDelinquentItems?id=1288
[Route("GetDelinquentItems")]
public async Task<IActionResult> GetDelinquentItems(int id)
{
var model = _checkListService.GetDelinquentItems(id);
return Ok(model);
}
That 'html gibberish' (indeed not the most elegant way to show an error) still contains some useful information. The first line says:
500 internal server error
and in the last three lines you can read:
Ambiguos HTTP method for action...CheckListController.GetDelinquentItems... Actions require explicit HttpMethod binding for Swagger
therefore another
[HttpGet("{id}")]
before the GetDelinquentItems() method should solve the problem.

Does StringContentProvider set Content-Type header in HTTP request?

I am trying to use Firebase Cloud Messaging by Google with the help of Jetty HTTP client:
public static final String FCM_URL = "https://fcm.googleapis.com/fcm/send";
public static final String FCM_KEY = "key=AAAA....";
private final HttpClient mHttpClient = new HttpClient();
private final CompleteListener mFcmListener = new CompleteListener() {
#Override
public void onComplete(Result result) {
if (result.isFailed()) {
// TODO delete FCM token in database for certain responses
}
}
};
mHttpClient.start();
mHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.content(new StringContentProvider(notificationStr), "application/json")
.send(mFcmListener);
My question is very simple, but I couldn't find the answer myself yet by looking at the StringContentProvider and its base classes -
If I need to set the request HTTP header for FCM:
Content-Type: application/json
then do I have to add the line:
.header(HttpHeader.CONTENT_TYPE, "application/json")
or will that class already do it for me?
A couple of points:
Yes, if you don't set content type header explicitly, it would be auto set based on the selected Content Provider.
By default, the StringContentProvider sets Content-Type to text/plain. To override, you need to use another constructor -
new StringContentProvider("application/json", content, StandardCharsets.UTF_8);
Request #setContent method auto sets Content-Type header to the provided value. Hence, you need not make any change to the code.

Is it possible to return a response from a Web API constructor?

I have a Web API ApiController base class and I would like to perform some validations in the constructor. This might include checking the current load on the server. If it's high, I'd like to return an appropriate HttpResponseMessage indicating the requestor should try again later.
Is something like this possible?
I Haven't tested it but that's not what the constructor is for. I don't think all plumbing is set at that time.
You could use global filters for this purpose. Here you have a sample that sets a global filter for authorization, you should use a similar logic but creating your own filter for your specific purposes.
A global filter would intercept all your requests and is executed before the controller actions so is a good place to perform your task.
Even though what you are doing sounds like it may be better to revise the approach. Note that you can throw HttpResponseException since the WebApi is Rest Service HttpResponseException is the recommended way to throw Exceptions back to the client.
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("No idea what happened "),
ReasonPhrase = "Something was not Not Found"
}
throw new HttpResponseException(resp);
As long as you're using .NET 4.5, then you'd be better off creating a custom MessageHandler. You'll need to extend DelegatingHandler in order to do that.
public class MyHandler : DelegatingHandler {
protected override async Task<HttpResponseMessage> SendAsync(
HttpMessageRequest request, CancellationToken cancellationToken) {
// Access the request object, and do your checking in here for things
// that might cause you to want to return a status before getting to your
// Action method.
// For example...
return request.CreateResponse(HttpStatusCode.Forbidden);
}
}
Then inside your WebApiConfig, just add the following code to use the new Handler:
config.MessageHandlers.Add(new MyHandler());
You can't throw HttpResponseException in constructor, that will always cause 500.
Easiest way is to override ExecuteAsync():
public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken) {
if (!myAuthLogicCheck()) {
// Return 401 not authorized
var msg = new HttpResponseMessage(HttpStatusCode.Unauthorized) { ReasonPhrase = "User not logged in" };
throw new HttpResponseException(msg);
}
return base.ExecuteAsync(controllerContext, cancellationToken);
}

Building workaround for HttpWebRequest timeout issue

I'm using the HTTP Web Request class to call a RESTful web service. I need to pass data and receive data and it all seems to work very well. Today I attempted to configure the time-out of the class because there is a high likelihood of the server running the service being offline and I don't want to waste time waiting. I configured it all but it seemed to make no difference. The call still waited over 10 seconds before failing.
On looking into it I found that the time-out only deals with the processing of the call and that the DNS lookup beforehand is not included. As this would be the problem it would make sense as to why the time-out wasn't working as I'd expected.
Further reading suggested using the HttpWebRequest class in an asynchronous style instead. I've had a look at the code to do so but don't understand how to retrieve the callback in my code which is effectively synchronous.
The code I have as follows is as so:
HttpWebRequest _serviceRequest = (HttpWebRequest)WebRequest.Create(new Uri("http://mywebservice.com"));
_serviceRequest.Timeout = 3000;
HttpWebResponse response = (HttpWebResponse)_serviceRequest.GetResponse();
XmlReader reader = XmlReader.Create(response.GetResponseStream(), set);
The code I have to call asynchronously ends with the following line, but I'm not sure as to what I should do to get the response object.
IAsyncResult result = (IAsyncResult)req.BeginGetResponse(new AsyncCallback(RespCallback), reqState);
I'm also concerned about a half baked asynchronous solution such as this. Is it good practice to use an asynchronous method through a synchronous piece of code.
Any helpers appreciated...
The response would be available when the callback function RespCallback is invoked. I don't know what reqState has, but I assume it contains a reference to the original HttpWebRequest object. If this is the case, this would be a simple implementation of the RespCallback method:
void RespCallback(IAsyncResult asyncResult)
{
ReqState reqState = (ReqState)asyncResult.AsyncState;
HttpWebResponse resp = (HttpWebResponse)reqState.Request.EndGetResponse(asyncResult);
// do what you need with the response.
}
Update: more info as asked in the comment
If you want the response in the same method where you did the Begin call, you can have an event which will be set when the callback is received, and you can wait on that event after the Begin call, like in the example below
class ReqState {
public HttpWebRequest Request { get; set; }
public HttpWebResponse Response { get; set; }
public AutoResetEvent Evt { get; set; }
}
void RespCallback(IAsyncResult asyncResult) {
ReqState reqState = (ReqState)asyncResult.AsyncState;
reqState.Response = (HttpWebResponse)reqState.Request.EndGetResponse(asyncResult);
reqState.Evt.Set();
}
void CallMethod() {
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(...);
// set properties on req
ReqState state = new ReqState();
state.Request = req;
state.Evt = new ManualResetEvent(false);
req.BeginGetResponse(RespCallback, state);
state.Evt.WaitOne(TimeSpan.FromSeconds(30)); // wait for 30 seconds
// access response via state.Response
}
Now notice that you're essentially doing a synchronous call in an asynchronous way. That gives you more control over the timeout, but with the price of code complexity. Another thing, this will not work on platforms such as Silverlight (and Windows Phone, IIRC), where synchronous calls (even those dressed up as asynchronous) are forbidden.