Getting POST data from WebAPI - c#

We're working on developing an application that uses Plivo for sending and receiving SMS messages. For every request that Plivo sends, they also send a signature in the HTTP header so that we can verify the request came from Plivo and not from a random user.
https://www.plivo.com/docs/xml/request/#validation
To do this validation, we require the POST content as a query string (eg: To=15555555555&From=11234567890&TotalRate=0&Units=1&Text=Text!&TotalAmount=0&Type=sms&MessageUUID=2be622bc-79f8-11e6-8dc0-06435fceaad7).
Current solution
This is what we have so far:
private bool VerifyPlivo(object thing, HttpRequestMessage Request)
{
if (Request.Headers.Contains("X-Plivo-Signature"))
{
Dictionary<string, string> reqParams = (from x in thing.GetType().GetProperties() select x).ToDictionary(x => x.Name, x => (x.GetGetMethod().Invoke(thing, null) == null ? "" : x.GetGetMethod().Invoke(thing, null).ToString()));
IEnumerable<string> headerValues = Request.Headers.GetValues("X-Plivo-Signature");
string signature = headerValues.FirstOrDefault();
return XPlivoSignature.Verify(Request.RequestUri.ToString(), reqParams, signature, plivoToken);
}
else
{
return false;
}
}
[Route("RecieveSMS")]
[HttpPost]
public HttpResponseMessage RecieveSMS(PlivoRecieveSMS req)
{
if (!VerifyPlivo(req, Request))
{
return new HttpResponseMessage(HttpStatusCode.Forbidden);
}
... // do actual work here
}
This works by using the object that it maps to PlivoRecieveSMS and doing some reflection to get the properties and values, and sticking them in a Dictionary. This works well especially given our lack of the preferred solution...
Preferred solution
Right now, we require a model (PlivoRecieveSMS) to map the data, and then do introspection to find the key/values. We would like to move the logic to an extension of System.Web.Http.AuthorizeAttribute, so that we can do something as simple as:
[AuthorizedPlivoApi]
[Route("RecieveSMS")]
[HttpPost]
public HttpResponseMessage RecieveSMS(PlivoRecieveSMS req)
{
... // do actual work here
}
The actual authorization is done in AuthorizedPlivoApi - if it's not valid, the request never reaches the controller. But we cannot do this at the moment because we can't map it to a specific object inside of AuthorizedPlivoApi.
I would like to access the POST key's / values directly, or perhaps map it to a dynamic object that isn't pre-defined before hand. If I can do that, we can then achieve our preferred solution.
tl;dr: is there any way to push application/x-www-form-urlencoded data from a POST request into a Dictionary<string,string>() without using a specific model?

Related

C# Type object as payload to Post Method

Can we upload a C# Type object to a Post Method ?
[HttpPost]
[Route("SendModelPayload")]
public async Task<IActionResult> SendModelPayload([FromBody] Type type)
{
// Do some stuff
return Ok("test");
}
But, Here type is null.
client code:
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(#"http://localhost:5000");
var res = httpClient.PostAsJsonAsync(#"/api/ModelPayload/SendModelPayload",
typeof(Student)).Result;
Any ideas, Thanks in advance !!
When transmitting data to a REST API, you need to keep in mind that client and server are basically unrelated in terms of technology and therefore use a common ground that a lot of programming languages support. In your case, there might be a .NET client and a .NET server, but it could well be that a Javascript client calls your API that does not know of the Type class. Even if you have .NET client and a .NET server, the client could send a type that the server is unaware of.
So you need to find a common ground that is supported by various programming environments and is flexible enough to also cope with the situation that the client sends data that the server cannot map to a type.
One way would be to not transmit a Type directly, but transmit the type name as a string. The server could then try to load the type directly. If the type can be found, it could work with the type, if not, it would return a BadRequestResult to notify the client of the invalid data.
[HttpPost]
[Route("SendModelPayload")]
public async Task<IActionResult> SendModelPayload([FromBody] string typeName)
{
// Try to load type
var type = Type.GetType(typeName);
if (type == null)
return BadRequest();
// Do some stuff
return Ok("test");
}
The client would send a type name instead of a real type e.g.
var res = httpClient.PostAsJsonAsync(#"/api/ModelPayload/SendModelPayload",
typeof(Student).AssemblyQualifiedName).Result;
By transmitting the AssemblyQualifiedName, the type name is very specific, so the server needs to be able to load the exact same type, but fail if it cannot laod the type.
If I understand your question, you want to upload a Student instance to the SendModelPayload endpoint?
If that is correct, then a couple of changes are necessary to get what you have working:
POST Method
// changed to accept Student object in body
[HttpPost]
[Route("SendModelPayload")]
public async Task<IActionResult> SendModelPayload([FromBody] Student type)
{
// Do some stuff
return Ok("test");
}
Client Code
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(#"http://localhost:5000");
// create instance and populate it with data
var studentInstance = new Student() { Name = "Student Name" };
// then call the post method and pass the instance of the Student class
// that will get serialized and sent as the body
var res = httpClient.PostAsJsonAsync(#"/api/ModelPayload/SendModelPayload",
studentInstance).Result;
Instead of Expecting TYPE from body. Convert string input to Type
[HttpPost]
[Route("SendModelPayload")]
public async Task<IActionResult> SendModelPayload([FromBody] string TypeName)
{
var ty = Type.GetType(TypeName);
if(ty != null)
Suggesting as I am not exactly aware with requirement

RestRequest Parameters adding logic

I am having some issues finding information about adding some logic field in my RestRequest using V 107. I am trying to add a filter to my GET query
dl_document_indexed_date gt '2020-12-07T08:30:42.483Z'
There are a few other queries in the call which i am using Dictionary<string, string> to store them, and it works great however it only works if i am looking for something equal to, as adding it to the parameters it seems by default its equal to and i am not finding any way to add any other logic, gt/ge/lt/le etc. using the older version i would just append the url adding the logic i need, but i am not seeing a way to append the url either. Looking over their documentation i either missed it, cant find it, or its not there. Any help would be greatly appreciated! My method looks like this
public static async Task<string> GET_API(String RequestUrl, string RequestObject, Dictionary<string, string> parameters)
{
var request = new RestRequest(RequestObject);
var options = new RestClientOptions(RequestUrl)
{
ThrowOnAnyError = true,
Timeout = -1
};
var client = new RestClient(options);
client.Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator("Bearer " + TokenManager.GetAccessTokenString("TRN"));
foreach (var parameter in parameters)
{
request.AddQueryParameter(parameter.Key, parameter.Value);
}
var response = await client.GetAsync(request);
return response.Content.ToString();
}
I send the BaseURL , the RequestObject would be table i am calling in the base URL, and my dictionary item contains the Field name, and the field values that i am dynamically generating on another method that would append the string. and example would be
parameters.Add("dl_document_name", "TableA");
which would append the URL with dl_document_name eq 'TableA'
it would call the API after i add the OAuth Token i create and return the data i need and send it back. or another option i guess could be appending the string with the logic i need to return the data
You should use OData, it's easy to implement and it has different kind of filters, you also can set which filters are usable and which aren't.
https://www.odata.org/
I figured out a work around, if i only have one i can add it to the first parameter and adding the filter as the first key, which will work unless i have multiple conditions that are not eq
parameters.Add("filter","dl_document_indexed_date gt '2020-12-07T08:30:42.483Z'");

ASP.NET Web API object parameter not being filled on post request

I am trying to set up a small ASP.NET Web API projects so I can post data to the database from a small React.JS project. I tried alot of sollutions but the results made no sense and I have no idea how to fix it anymore.
I have this very simple model:
public class Hour
{
public int WeekID { get; set; }
}
And this is my controller
[HttpPost]
public IHttpActionResult AddHour(Hour hour)
{
return Ok();
}
This is the method that I use to POST my data
export const SaveWeek = weekData=> {
const headers = new Headers();
headers.append("Content-Type", "application/json");
const Week= {
method: "POST",
headers,
mode: "cors",
body: weekData
};
console.log("Hours:");
// Returns {"WeekID": 1}
console.log(Hours.body);
return axios.post("http://localhost:52350/api/REST/AddHour", {
Week
});
};
The way I call this SaveWeek method in React is:
// The JSON parameter is for testing hard coded to: {"WeekID": 1}
handleSave = async json => {
const data = await SaveWeek(json);
console.log(data);
this.closeModal();
};
I know that the axios POST request works, the way I tested that is by changing the method to not use any parameters and looking at the result that where received:
[HttpPost]
public IHttpActionResult AddHour(Hour hour)
{
// This returns a string in which the data that I sent
// can be found.
string body = Request.Content.ReadAsStringAsync().Result;
return Ok();
}
The weird thing is that the body will be filled with data when the method does not contain any parameters, but when I provide the method with the Hour object parameter the body will be an empty string (""). And also the Hour object parameter wont be filled with the values that I provide it.
What am I doing wrong here?
According to https://github.com/axios/axios#axiosposturl-data-config axios.post has following signature
axios.post(url[, data[, config]])
So you just need to change your request to
export const SaveWeek = weekData => {
//headers should be simple object, not Headers
const headers = {
"Content-Type": "application/json"
};
//removed body, because we pass data as second parameter
//removed method, because 'axios.post' implies using "post" method
const Config = {
headers,
mode: "cors"
};
const url = "http://localhost:52350/api/REST/AddHour";
return axios.post(url, weekData, Config);
}
An incoming request to the ASP.Net Web API pipeline is read as a forward-only stream for super speed. Once it has been read it cannot be read again.
[HttpPost]
public IHttpActionResult AddHour(Hour hour)
{
// With model binding
// use hour.WeekID
}
In this first example model binding is already done and once it has been read it cannot be read again. Hence, Request.Content will be empty after that.
[HttpPost]
public IHttpActionResult AddHour()
{
// Without model binding
// use Request.Content
}
In second example it does not use model binding therefore still has the Request.Content property populated.
Use one or the other, not both, do not mix with MVC model binding which works differently.
A better explanation is available in this blog post
http://www.hackered.co.uk/articles/asp-net-web-api-why-is-the-request-Content-empty-when-the-model-is-populated

GraphQL should call HTTP or API directly?

I want to use GraphQL in my project, so I want to know which one is the better option:
public class UserQuery : ObjectGraphType<object>
{
public UserQuery(UserData data)
{
Name = "Query";
Field<User>("user", resolve: context => data.GetUser(1));
}
}
So for implementing data.GetUser(1), I have a two options:
Option 1: Call Http endpoint and fetch the response through the HttpResponse
var client = new Restclient("BaseUrl");
client.Execute(new RestRequest("api/v1/account/1"));
Advantage: Doen't need to validate the request and response, also all custom filtering and exception handling will be applied automatiaccly*
Disadvantage: Double Http call, also return bigger response*
Option 2: Call Api directly through the Controller
var controller = new AccountController();
controller.GetById(1);
Advantage: Return lighter object and better performance*
Disadvantage: Have to apply all validations and exceptions handling again.*

Postman Testing send string to Web Api accepting string is null

I have been testing all the Get,Create,Update methods with Postman in which the Get passes in nothing. The Create and Update passes in raw json with Activity object with several properties that do match up with the C# class
So this signature for Create and Update works fine
[HttpPost]
public IHttpActionResult UpdateActivity(Activity activity)
Above works with Postman passing in JSON content type with all the properties. I have done this on OTHER projects.
HOWEVER
I'm trying to simply pass in a string and it is null no matter what
public IHttpActionResult DeleteActivity([FromBody]string Id)
{
// delete
var del = ActivityService.DeleteActivity(Id);
return Ok(del);
}
Postman I tried MANY ways
http://localhost:49810/api/activityapi/deleteactivity
I have tried MANY many ways based on blogs and google search one such example
{ "Id" = "5808786fa3e9ec79546b3c71" }
I know this is an older question but I wanted to help those who might have a similar problem as I was able to get this working.
In WebAPI Controller my method is setup as
[HttpPost]
public IHttpActionResult Create([FromBody] int eventId)
{
....
}
In order to get this to test properly in Postman you have to: In body, set to raw, make sure JSON (application/json) is set and then just add value like 2 that's it.. not like { "eventId":2 } which is proper JSON just the value and then it will work.
So in original poster's case, in Postman, if you set Body to raw, JSON (application/json) then "5808786fa3e9ec79546b3c71" as value it will work.
In Postman ensure the body is set to raw and select json and in the body just write "your string" in quotes. Do not use {} to surround it because that is to make a complex object
Try the following in the body, with the content-type as application/json
{ "5808786fa3e9ec79546b3c71" }
As when you specify it like so, it will attempt to de-serialize into a complex type with a property of Id
{ "Id" : "5808786fa3e9ec79546b3c71" }
Old question, but for those still wondering, I would recommend sending your string as a query parameter. Take a method like this for example:
[AllowAnonymous]
[HttpGet("resendEmailConfirmtionLink")]
public async Task<IActionResult> ResendEmailConfirmationLink(string email)
{
var user = await _userManager.FindByEmailAsync(email);
if (user == null) return Unauthorized();
var origin = Request.Headers["origin"];
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
token = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(token));
var verifyUrl = $"{origin}/verifyEmail?token={token}&email={user.Email}";
var message = $"<p>Please click the below link to verify your email address:</p><p><a href='{verifyUrl}'>Click to verify email</a></p>";
await _emailSender.SendEmailAsync(user.Email, "Please verify email", message);
return Ok("Email verification link resent");
}
This method expects a key value pair of a string called email. You can send your request like "http://localhost:5000/api/account/verifyEmail?email=myemail#test.com" or, in Postman, add it as a parameter like this:
postman query params
Your payload is not valid.
Change-->
{ "Id" = "5808786fa3e9ec79546b3c71" }
To-->
{ "Id" : "5808786fa3e9ec79546b3c71" }
[HttpPost]
public IHttpActionResult Create(int eventId)
{
....
}
Use form-data instead of raw-json
Key - eventId
Value - "5808786fa3e9ec79546b3c71"
This worked for me.

Categories

Resources