ASP .NET Web API reading inputstream twice - c#

I am using ASP .NET Web API and I have a Controller that have code similar to this:
[Route("UpdateData")]
[HttpPost]
public HttpResponseMessage UpdateData([FromBody]RequestClasses.UpdataData data)
{
string json;
if (HttpContext.Current.Request.InputStream.Length > 0)
{
HttpContext.Current.Request.InputStream.Position = 0;
using (var inputStream = new StreamReader(HttpContext.Current.Request.InputStream))
{
json = inputStream.ReadToEnd();
}
}
// Dencrypt json
return Request.CreateResponse(HttpStatusCode.OK);
}
As input parameter I have "[FromBody]RequestClasses.UpdataData data". I have this in order to be able to show a Help page (using Microsoft.AspNet.WebApi.HelpPage).
The data object received in this method is encrypted and I need to decrypt it.
My problem is that I cannot call HttpContext.Current.Request.InputStream because the "[FromBody]RequestClasses.UpdataData data" has disposed my InputStream.
Any good ideas to solve this? As I still need the help page to show which parameters to call the method with.

By design ASP.NET Web API can only read the payload input stream once. So, if the parameter binder reads it, you don't have it available. That's way people is telling you in the comments to use parameterless methods, and read the payload yourself.
However, you want to have parameters to see them in the help page. There is a solution for that: do the decryption of the request in previous steps. To do that you can use Message handlers. Please, see this: Encrypt Request/Reponse in MVC4 WebApi

Related

C# .Net API Post Call Using HttpResponseMessage Post([FromBody] is always null

I'm new to writing .net APIs and I'm working in Visual Studio 2017. I've been working on this for a couple of days and I'm completely stumped. I'm trying to create a simple web API that a Post call sends a cXML string passed into it via the Post Body. I then take the incoming cXML string and simply save it to a text file on a network drive. That is all I need to do, I don't need to de-serialize the xml, read any of the fields or extract any data out of the XML, I just need to grab the entire input xml string and save it to a text file. The problem I'm having is no matter what I've tried the incoming body always seems to be null. My code is simple:
[HttpPost]
[Route("api/Pass_XML_to_File")]
public HttpResponseMessage Post([FromBody] dynamic IncomingXML)
{
//do work here: take Incoming xml string and save it to a file which should be simple...
}
Unfortunately my IncomingXML variable is always null, so I have no data to save into a text file. I've been testing this from Postman and no matter what I've tried the variable is always null.
I've tried many other ways such as
Post([FromBody] XmlDocument IncomingXML)
Post([FromBody] string IncomingXML), etc.
I've tried changing in Content-Type header in Postman from application/xml, text/xml, text and a few others without any success. The funny thing is if I pass a JSON string in the body (changing the Content-Type to text/JSON) the data comes in perfectly without issue. Just when I pass xml the incoming body is always null.
Does anyone know how I can get the body xml to come in as a string so I can simply save it to a text file for later processing on a separate system? Thank you all in advance for your assistance.
Can you post the form you sent on the client side?
[HttpPost]
[Route("api/Pass_XML_to_File")]
public HttpResponseMessage Post([FromBody] dynamic IncomingXML)
{
// data is coming in correctly.
return null;
}
Postman Client Send :
{
"IncomingXML":"<note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don'tforgetmethisweekend!</body></note>"
}
data comes to variable successfully.
if you want to accept a xml format request, you should do the below steps:
Startup.ConfigureServices edit:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddXmlSerializerFormatters();
}
Apply the Consumes attribute to controller classes or action methods that should expect XML in the request body.
[HttpPost]
[Route("api/Pass_XML_to_File")]
[Consumes("application/xml")]
public HttpResponseMessage Post([FromBody] dynamic IncomingXML)
{
// data is coming in correctly.
return null;
}
Note: Install the Microsoft.AspNetCore.Mvc.Formatters.Xml NuGet package.

C# MVC Download SSRS Report as PDF File passing Parameter data as Post Request

I've set up a short test for creating a SSRS report and Downloading it as PDF via an MVC Website.
private static string _report = #"http://myServer/ReportServer?/Test&s:Command=Render&rs:Format=pdf&Param1={0}&Param2={1}";
public FileResult DownloadReport() {
using (var client = new WebClient()) {
client.Credentials = CredentialCache.DefaultCredentials;
var data = client.DownloadData(String.Format(_report, "MyParam1Value", "MyParam2Value");
return File(data, "application/pdf", "YourReport.pdf");
}
}
This is working fine so far.
But for the Report im planning to do i will have a lot of Parameters with large data.
So im worried that im reaching the maximum lenght of the URL.
Is there a way to pass the Parameter data as POST request, so that its not in the URL?
Thank you all
I have found a Solution, or workaround.. :-)
I will store all the data in a Db an just pass the id of the created record to the report.
It was for my usecase not neccessary to store the data in a Db, but its also not a problem.
If someone still would know how to pass the parameters via Post Request, then feel free to answer. Maybe someone else needs it.
Thank you all

How to create a web Api have http post function which can take any dynamic json content from body and able to parse it into string

I have an third party event that post data to my hosted API but the problem is I don't know the Json structure which event posting to my API from Body. I need to read the json content posted by Event to my API.
I have tried to create post method as dynamic or string type the event able to call the service but data is not get typecast, it shows null always.
[HttpPost]
RecievedPayload([FromBody]dynamic json)
{
}
[HttpPost]
RecievedPayload([FromBody]string json)
{
}
RecievedPayload Api method is getting invoked by Third party Event but the json content is null. I need to know the Json structure so that I can make a custom class to hold the content.
There isn't a simple way to just get the whole body as a parameter.
This blog post is worth a full read: https://weblog.west-wind.com/posts/2013/dec/13/accepting-raw-request-body-content-with-aspnet-web-api
However to accomplish your goal, this is a slightly modified version of a code sample provided in the blog post, which should work for your needs:
[HttpPost]
public async Task<string> RecievedPayload()
{
string result = await Request.Content.ReadAsStringAsync();
return result;
}
Also worth reading: https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

How to read parameter from Request.Body in ASP.NET Core

Context: My application is behind a central login app, whenever the user apply access to my application, my application got a http request contain the user info. And I need to retrieve the user info from the HttpRequest Body.
This is what I tried so far:
currentContext.HttpContext.Request.Query["user-name"].toString(); // got nothing
using (var reader = new StreamReader(currentContext.HttpContext.Request.Body))
{
var body = reader.ReadToEnd();
} // I can get the raw HttpRequest Body as "user-name=some&user-email=something"
Is there any method I can use to parse the parameters and values from the Request.Body?
I tried the following, got nothing either.
HttpContext.Item['user-name'] \\return nothing
Request.Form["user-name"] \\ return nothing
and the reason I am not be able to use model binding is, in the HttpRequest body, the key name is "user-name", and in c#, I can't create a variable with a "-"
Meanwhile, in the my .net 4.6 application, Request["KeyName"].toString() works just fine.
I figured out a way to convert the raw HttpRequest Body to a Query String, then read parameters from it.
Here is the code:
var queryString = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(requestBody);
string paramterValueIWant = queryString["KeyName"];
There is one problem though, when the KeyName doesn't exist in the body, it will throw an exception. So you have to null check or do a try catch.
Still I feel like there should be a better way to read the parameter, as I mentioned, in my .net 4.6 application, all I need to do is Request["KeyName"].
Assuming that we are talking about POST/PUT/PATCH call, you can use
Request.Form["KeyName"]
in your API method and set the 'contentType' of the Ajax request as application/x-www-form-urlencoded
Notice that Request is automagically available inside your method. No need to explicit call it.
When using GET/DELETE call i prefer to use
[HttpGet("{UserId}")] // api/User/{UserId}
public IActionResult Get(int UserId){
// do stuff calling directly UserId
}
Or with PUT/PATCH
[Route("User/{EntityId}/{StatusFilter}")] // api/User/{EntityId}/{StatusFilter}
[HttpPut]
public IActionResult Put(int EntityId, int StatusFilter){
// do stuff calling directly EntityId and StatusFilter
}
where you can then still take data from the Body using Request.Form["KeyName"]

Using WebClient to pass arrays as part of message body for post action

I am writing a console application that needs to perform a POST to an MVC controller. I am using the WebClient class to perform the POST. But I'm having trouble understanding how to add arrays to the message body.
For simple parameters, it seems to work if I do this:
using (var client = new WebClient())
{
var values = new NameValueCollection
{
{ "userName", "userName" },
{ "password", "passwordGoesHere"}
};
byte[] responseArray = client.UploadValues(String.Format("{0}/Mobile/StartSession", serverAddress), values);
Debug.WriteLine(String.Format("\r\nResponse received was :\n{0}\n", Encoding.ASCII.GetString(responseArray)));
}
I was trying to find how to pass arrays in the message body when using WebClient (for calling one of the other methods). I came across this solution: POST'ing arrays in WebClient (C#/.net)
It appears the solution actually passes parameters in the query string (and not in the message body). This seems to work in any case, as the HttpPost method on the MVC controller is still receiving the correct information. However, another method requires that I pass an image as an array of bytes. This is too large to be passed in the querystring and so the call fails.
So my question is, using the code I provided above, how can I add arrays in there as well. So an array of bytes for example, but also an array of strings.
If any one can provide me with a solution it would be much appreciated, or if I'm incorrect in my thinking please let me know.
Thanks
Instead of using array of bytes maybe you should POST a file in the same way files are uploaded from browser from file inputs. This way you will save some transfered bytes, but you have to use HttpWebRequest instead of WebClient. More about this solution is here:
Upload files with HTTPWebrequest (multipart/form-data)
You upload bytes as "multipart/form-data" content type. On the server you will receive the streams of bytes in Request.Files collection.

Categories