How to read RequestBody by PipeReader and return start position of stream to zero (Net core 6) - asp.net-core

I need access to RequestBody inside OnActionExecuting or OnActionExecuted filter, but I can not found concrete example how to use PipeReader to read full request body stream and return stream position to zero (in order to read request parameters by ControllerBase - if I read body OnActionExecuting or firstly return position to zero if I read in OnActionExecuting).
In my attribute Body always empty.
However, API parameters is present.
Or maybe there is another way to receive Request Body in Action Filter for ControllerBase?

From your description, I think you wanna get request body in multiple times in asp.net core. But in asp.net core, the request can not be read once it is consumed. If you want to read the request body multiple times, you need to set:
context.Request.EnableBuffering()
Then to read the body stream you could for example do this:
string bodyContent = new StreamReader(Request.Body).ReadToEnd();
On the safer side, set the Request.Body.Position reset to 0. That way any code later in the request lifecycle will find the request body in the state just like it hasn’t been read yet.
Request.Body.Position = 0;
So you can set this code in either OnActionExecuting or OnActionExecuted method.
public void OnActionExecuting(ActionExecutingContext context)
{
context.HttpContext.Request.EnableBuffering();
string bodyContent = new StreamReader(context.HttpContext.Request.Body).ReadToEnd();
context.HttpContext.Request.Body.Position = 0;
}
But please note that, Model binding happens before action filter, So Model binding will consume request body first and you can not read it in your action filter, You need to custom model binding and Apply the above configuration to it.

Related

Customize aspnet core routing attribute so that Url.Action() returns a different url?

This is an example of what I want to achieve, however I want to do my own custom attribute that also feeds itself from something other than the request url. In the case of HttpGet/HttpPost these built-in attributes obviously have to look at the http request method, but is there truly no way to make Url.Action() resolve the correct url then?
[HttpGet("mygeturl")]
[HttpPost("myposturl")]
public ActionResult IndexAsync()
{
// correct result: I get '/mygeturl' back
var getUrl = Url.Action("Index");
// wrong result: It adds a ?method=POST query param instead of returning '/myposturl'
var postUrl = Url.Action("Index", new { method = "POST" });
return View();
}
I've looked at the aspnet core source code and I truly can't find a feature that would work here. All the LinkGenerator source code seems to require routedata values but routedata always seems to require to be in the url somewhere, either in the path or in the query string. But even if I add the routedata value programmatically, it won't be in time for the action selection or the linkgenerator doesn't care.
In theory what I need is to pass something to the UrlHelper/LinkGenerator and have it understand that I want the url back out that I defined in my custom attribute, in this case the HttpPost (but I'll make my own attribute).

Locale aware bean validation message interpolation at ExceptionMapper in openliberty

I have a JAX-RS #POST endpoint whose input data has to be #Valid:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response myEndpoint(#javax.validation.Valid MyInputData input) { /*...*/ }
With MyInputData class annotated with many constraints:
#lombok.Data
public class InputData {
#Size(min = 1, max = 3)
private String someString;
/* ... */
}
Beyond that I have an ExceptionMapper<ConstraintViolationException> that transform the Exception into a Collection<String> (basically every single ConstraintViolation transformed to String using its getMesssage() method), then returns a Response.status(Status.BAD_REQUEST).entity(list).build().
Everything is working nicely. Fed an invalid input and I get back a HTTP 400 with a nice array of constraint violations in json format.
So far, so good...
BUT... the messages are in server's locale. Even if HTTP post sends a Accept-language header (and it is correctly detected when getting HttpServletRequest::getLocale).
By the time the ExceptionMapper gets hold of ConstraintViolation every message has already been interpolated, so no chance set the client locale.
Since the validation runs even before the JAX-RS resource (indeed, the JAX-RS resource isn't even called in case of invalid input), this locale aware message interpolator must be configured somewhere else.
Where? Is there already a MessageInterpolator implementation whose operation takes the HttpServletRequest locale into account?

Efficient way to bring parameters into controller action URL's

In ASP.Net Core you have multiple ways to generate an URL for controller action, the newest being tag helpers.
Using tag-helpers for GET-requests asp-route is used to specify route parameters. It is from what I understand not supported to use complex objects in route request. And sometimes a page could have many different links pointing to itself, possible with minor addition to the URL for each link.
To me it seems wrong that any modification to controller action signature requires changing all tag-helpers using that action. I.e. if one adds string query to controller, one must add query to model and add asp-route-query="#Model.Query" 20 different places spread across cshtml-files. Using this approach is setting the code up for future bugs.
Is there a more elegant way of handling this? For example some way of having a Request object? (I.e. request object from controller can be put into Model and fed back into action URL.)
In my other answer I found a way to provide request object through Model.
From the SO article #tseng provided I found a smaller solution. This one does not use a request object in Model, but retains all route parameters unless explicitly overridden. It won't allow you to specify route through an request object, which is most often not what you want anyway. But it solved problem in OP.
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="#Context.GetQueryParameters()" asp-route-somestring="optional override">Link</a>
This requires an extension method to convert query parameters into a dictionary.
public static Dictionary GetQueryParameters(this HttpContext context)
{
return context.Request.Query.ToDictionary(d => d.Key, d => d.Value.ToString());
}
There's a rationale here that I don't think you're getting. GET requests are intentionally simplistic. They are supposed to describe a specific resource. They do no have bodies, because you're not supposed to be passing complex data objects in the first place. That's not how the HTTP protocol is designed.
Additionally, query string params should generally be optional. If some bit of data is required in order to identify the resource, it should be part of the main URI (i.e. the path). As such, neglecting to add something like a query param, should simply result in the full data set being returned instead of some subset defined by the query. Or in the case of something like a search page, it generally will result in a form being presented to the user to collect the query. In other words, you action should account for that param being missing and handle that situation accordingly.
Long and short, no, there is no way "elegant" way to handle this, I suppose, but the reason for that is that there doesn't need to be. If you're designing your routes and actions correctly, it's generally not an issue.
To solve this I'd like to have a request object used as route parameters for anchor TagHelper. This means that all route links are defined in only one location, not throughout solution. Changes made to request object model automatically propagates to URL for <a asp-action>-tags.
The benefit of this is reducing number of places in the code we need to change when changing method signature for a controller action. We localize change to model and action only.
I thought writing a tag-helper for a custom asp-object-route could help. I looked into chaining Taghelpers so mine could run before AnchorTagHelper, but that does not work. Creating instance and nesting them requires me to hardcode all properties of ASP.Net Cores AnchorTagHelper, which may require maintenance in the future. Also considered using a custom method with UrlHelper to build URL, but then TagHelper would not work.
The solution I landed on is to use asp-all-route-data as suggested by #kirk-larkin along with an extension method for serializing to Dictionary. Any asp-all-route-* will override values in asp-all-route-data.
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="#Model.RouteParameters.ToDictionary()" asp-route-somestring="optional override">Link</a>
ASP.Net Core can deserialize complex objects (including lists and child objects).
public IActionResult HelloWorld(HelloWorldRequest request) { }
In the request object (when used) would typically have only a few simple properties. But I thought it would be nice if it supported child objects as well. Serializing object into a Dictionary is usually done using reflection, which can be slow. I figured Newtonsoft.Json would be more optimized than writing simple reflection code myself, and found this implementation ready to go:
public static class ExtensionMethods
{
public static IDictionary ToDictionary(this object metaToken)
{
// From https://geeklearning.io/serialize-an-object-to-an-url-encoded-string-in-csharp/
if (metaToken == null)
{
return null;
}
JToken token = metaToken as JToken;
if (token == null)
{
return ToDictionary(JObject.FromObject(metaToken));
}
if (token.HasValues)
{
var contentData = new Dictionary();
foreach (var child in token.Children().ToList())
{
var childContent = child.ToDictionary();
if (childContent != null)
{
contentData = contentData.Concat(childContent)
.ToDictionary(k => k.Key, v => v.Value);
}
}
return contentData;
}
var jValue = token as JValue;
if (jValue?.Value == null)
{
return null;
}
var value = jValue?.Type == JTokenType.Date ?
jValue?.ToString("o", CultureInfo.InvariantCulture) :
jValue?.ToString(CultureInfo.InvariantCulture);
return new Dictionary { { token.Path, value } };
}
}

Uploading a file in Jersey without using multipart

I run a web service where I convert a file from one file format into another. The conversion logic is already functioning but now, I want to query this logic via Jersey. Whenever file upload via Jersey is addressed in tutorials / questions, people describe how to do this using multipart form data. I do however simply want to send and return a single file and skip the overhead of sending multiple parts. (The webservice is triggered by another machine which I control so there is no HTML form involved.)
My question is how would I achieve something like the following:
#POST
#Path("{sessionId"}
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#PathParam("sessionId") String sessionId,
#WhatToPutHere InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFile); // returns StreamingOutput - works!
}
How do I get hold of the uploadedFileStream (It should be some annotation, I guess which is of course not #WhatToPutHere). I figured out how to directly return a file via StreamingOutput.
Thanks for any help!
You do not have to put anything in the second param of the function; just leave it un-annoted.
The only thing you have to be carefull is to "name" the resource:
The resource should have an URI like: someSite/someRESTEndPoint/myResourceId so the function should be:
#POST
#Path("{myResourceId}")
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#PathParam("myResourceId") String myResourceId,
InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFileStream);
}
If you want to use some kind of SessionID, I'd prefer to use a Header Param... something like:
#POST
#Path("{myResourceId}")
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#HeaderParam("sessionId") String sessionId,
#PathParam("myResourceId") String myResourceId,
InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFileStream);
}

Add custom header into Security element with WCF

Can I add and read a custom header in the Envelope/Header/Security element? I tried using the MessageHeader attribute, but that does not allow me to put the header in the Security element.
I created a class that implements IClientMessageInspector thinking that I could access the Security header like so:
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
Message originalMessage = buffer.CreateMessage();
foreach (MessageHeader h in originalMessage.Headers)
{
Console.WriteLine("\n{0}\n", h);
}
return null;
}
But the Security header is not present in the originalMessage.Headers object.
Create a custom message encoder: http://msdn.microsoft.com/en-us/library/ms751486.aspx.
You can access the message headers in your encoder's WriteMessage override. Note that the Message's Headers property will not contain the Security header (though this may depend on the type of security you're using). Write out the message to a stream or file using, say, Message.WriteMessage(XmlWriter). The stream/file will contain the contents of the message just before being sent over the wire, including the Security element. From there, you can modify your message as necessary and return an ArraySegment including your changes.