I have a method in Web API that I used the object as input, but when I try to run the API using URI the fields inside the object are Null.
this is my method:
[HttpGet]
[Route("AddUser/{user}")]
public async Task<string> CreateUser([FromUri]AddUser user)
{
//LoansApiTrace.Trace.Verbose(EventId.Start, () => string.Format("{0}: {1}", "AddUser", user.));
string Exception = await Repository.AddUserAsync(user);
return Exception;
}
This is AddUser object:
public class AddUser
{
public string UserEmailAddress { get; set; }
public string PasswordHash { get; set; }
public string Salt { get; set; }
public string RemoteRefNumber { get; set; }
}
and this is the URI:
http://localhost:59509/Adduser/user=test#yahoo.com,pass,salt,remref/
it goes to the method but UserEmailAddress , PasswordHash ,..all 4 are empty.
This is a really a bad practice to pass secret data through URI like you're doing. Then I will not attempt to give a solution for that to work.
The best practice is to pass that kind of data through your request body and use Http POST method :
[HttpPost]
[Route("AddUser/{userId}")]
public async Task<string> CreateUser(string userId, [FromBody]AddUser user)
{
// Find a user by userId
// Then update the user data.
}
you use an URI like this => http://localhost:59509/Adduser/12345 where 12345 is the user id.
you need to make sure that the selected HTTP method is POST
you need to write the data of AddUser into the request body
It also recommanded to use HTTPS when user need to send that type of data.
Consider Using POST If Applicable
While it may not be do-able, you may want to consider adding these fields within a <form> and simply posting them to the server in the body as opposed to using the actual URL itself. Passwords, salts and hashes generally aren't something that you want to get passed around like that.
If You Must Use A GET
Have you tried passing the values in as proper query-string parameters instead?
http://localhost:59509/Adduser/user?UserEmailAddress=test#yahoo.com&PasswordHash=abc&Salt=123&RemoteRefNumber=foo
This should set the following properties based on your current routes:
user = "user"
UserEmailAddress = "test#yahoo.com"
PasswordHash = "abc"
Salt = "123"
RemoteRefNumber = "foo"
MVC has to have some idea of how to bind these properties to those on your class, so unless the names match as expected, it will not know how to map something like "user" to "UserEmailAddress". As mentioned earlier, this isn't ideal and can present all sorts of security issues (so only use something like this on prototype / non-production environments).
Related
I've been trying to read the body of a post request in a controller for a while but I keep getting the error that synchronous actions are not allowed. This is also meant to be used as an API so I can't just read the values trough a textbox like normal.
This is my code right now but I have no idea if this is right because this is based on Java code and my experience with asp.net core is limited.
public IActionResult Test()
{
string body = "";
StreamReader reader = new StreamReader(Request.Body);
while (reader.ReadLine() != null)
{
body += reader.ReadLine();
}
return Ok(body);
}
I'm just trying to read the body so I have an idea on how I can filter on the header of the json input.
Is there maybe a different way that I can do this? Or am I forgetting something important?
Thanks to the advice of Chetan Ranpariya, I found the answer. Like Chetan said I needed to use a model class which is pretty easy to do. I just added a folder named Models and added a class name User with this code:
public class User
{
public string UserName { get; set; }
public string Password { get; set; }
}
And then my previous method turned into this:
public IActionResult Test(User user)
{
return Ok(new { username = user.UserName, password = user.Password });
}
The only thing that I needed to pay attention to was the json body of my request. The header needs to correspond with the property name so it looked like this:
{
"UserName": "Admin",
"Password": "AdminPass"
}
And now I can continue working on my project.
I'm using C# web api and want to create a filter to all requests.
I have a designated class to every request so I just want to add some data annotations and get the validation over with.
The problem is that I'm getting true every time on actionContext.ModelState.IsValid
I have added my filter in the config:
config.Filters.Add(new RequestValidationFilter());
validation method looks like every other in the web
public class RequestValidationFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid == false)
{
var errors = actionContext.ModelState
.Values
.SelectMany(m => m.Errors
.Select(e => e.ErrorMessage));
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
actionContext.Response.ReasonPhrase = string.Join("\n", errors);
}
}
}
I have the following method:
[HttpPost, Route("User/Login")]
public async Task<Responses.Login> Login(Requests.Login request)
{
...//some login logic
}
And also, I have my model which is:
public class Requests
{
public class Login
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Email address cannot be empty!")]
[MinLength(5)]
public string Email;
[Required]
public string Password;
}
}
I'm sending both an empty request or request which Email and Password are null and still the actionContext.ModelState.IsValid evaluate it as true
Attached is an image when email was sent but password wasn't.
Following a comment, here is my request via Advanced Rest Client chrome plugin
NOTE
the image actually shows that Keys and Values are empty when in fact they are supplied..
EDIT
number of things i've also tried:
removing all other filters, why? maybe the context was messed up by another reading.
sending valid request in terms of fields, but email was 1 char long.why? maybe Requiredis working differently than others, still nothing about the min-length issue.
instead of nested objects, i created a seperate stand-alone class for the Login object. why? thought maybe the fact that it's nested the validation is not recursive.
Looping the Arguments list one-by-one and validate as object, answer is always true. never fails, why? cause Im almost admitting defeat.
instead of adding filter to config as i described in the question, tried GlobalConfiguration.Configuration.Filters.Add(new RequestValidationFilter()); instead
You need to add { get; set; } after your model properties:
public class Login
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Email address cannot be empty!")]
[MinLength(5)]
public string Email { get; set; }
[Required]
public string Password { get; set; }
}
This is necessary because the default model validation for ASP.NET only includes properties with a public get method. From PropertyHelper.cs, here's some of the code which determines whether a property on the model will be included in validation:
// Indexed properties are not useful (or valid) for grabbing properties off an object.
private static bool IsInterestingProperty(PropertyInfo property)
{
return property.GetIndexParameters().Length == 0 &&
property.GetMethod != null &&
property.GetMethod.IsPublic &&
!property.GetMethod.IsStatic;
}
This method is used to filter the properties that are used in the default model binding in MVC/Web API. Notice that it's checking whether the GetMethod exists on the property and that it's public. Since your class didn't have the get methods on its properties, they were being ignored.
If you want to know more, you can kill a lot of time looking through the ASP.NET MVC source. I think the code on github is for a newer version of ASP.NET, but it seems like a lot of the same logic applies in the version you are using.
I'm currently developing a SPA in Angular, and so I've created a REST service using ServiceStack. I am also using ServiceStack's default authentication and authorization solution, which allows me to decorate services with the Authenticate attribute, and also allows me to authorize roles.
However, since my application has users, and users own resources, I need a way to restrict non-authorized users from performing certain actions. Furthermore, I would like to be able to create a single service for each discrete entity which can properly figure out what is safe to write to the database and what is safe to return to the user depending on their level of authorization.
So as an example, let's say I've created a service to handle operations on a Group entity. One of the actions I allow on a Group is to get the details for it:
Route: api/groups/{Id}
Response: Name, Description, CoverImageUrl, Members
However, depending on who the user is, I wish to restrict what data is returned:
Not authenticated: Name, CoverImageUrl
Authenticated: Name, CoverImageUrl, Decription
Member of requested group: Full access
Admin of website: Full access
So one simple approach to doing this is to create 3 different response DTOs, one for each type of response. Then in the service itself I can check who the user is, check on their relation to the resource, and return the appropriate response. The problem with this approach is that I would be repeating myself a lot, and would be creating DTOs that are simply subsets of the "master" DTO.
For me, the ideal solution would be some way to decorate each property on the DTO with attributes like:
[CanRead("Admin", "Owner", "Member")]
[CanWrite("Admin", "Owner")]
Then somewhere during the request, it would limit what is written to the database based on who the user is and would only serialize the subset of the "master" DTO that the user is permitted to read.
Does anyone know how I can attain my ideal solution within ServiceStack, or perhaps something even better?
The direct approach is the easiest, but you could also take advantage of custom filters attributes.
[Route("/groups/{Id}"]
public class UpdateGroup
{
public int Id { get; set; }
public string Name { get; set; }
public string CoverImageUrl { get; set; }
public string Description { get; set; }
}
[RequiresAnyRole("Admin", "FullAccess")]
[Route("/admin/groups/{Id}"]
public class AdminUpdateGroup
{
public int Id { get; set; }
public string Name { get; set; }
public string CoverImageUrl { get; set; }
public string Description { get; set; }
//... other admin properties
}
Service implementation:
public object Any(UpdateGroup request)
{
var session = base.SessionAs<AuthUserSession>();
if (session.IsAuthenticated) {
//.. update Name, CoverImageUrl, Description
}
else {
//.. only update Name, CoverImageUrl
}
}
public object Any(AdminUpdateGroup request)
{
//... Full Access
}
What ended up being the most pragmatic solution for me was actually pretty simple. The basic idea is that whichever service requires row-level authorization should implement a GetUserRole method, which in my case returns the user's most permissive role.
protected string GetUserRole(Domain.Group entity)
{
var session = SessionAs<AuthUserSession>();
var username = session.UserName;
if (session.Roles.Contains("Admin"))
{
return "Admin";
}
if (entity.Id == default(int) || entity.Leader.Username.Equals(username))
{
return "Leader";
}
// More logic here...
return session.IsAuthenticated ? "User" : "Anonymous";
}
Then I can use the user's role to figure out what to let them write:
var entityToWriteTo = ... // code that gets your entity
var userRole = GetUserRole(entityToWriteTo);
if (new[] {"Admin"}.Contains(userRole))
{
// write to admin-only entity properties
}
if (new[] {"Admin", "Leader"}.Contains(userRole))
{
// write to admin or leader entity properties
}
// Etc.
And the same logic applies for reads: You populate a DTO with properties set conditionally based on their role. Later on when you return the DTO back to the client, any properties that you haven't set either won't be serialized or will be serialized with a null value.
Ultimately, this solution allows you to use a single service for a resource instead of creating multiple services each with their own request DTO. There are, of course, refactorings you can do that makes this solution more streamlined. For example, you can isolate all of your reads and writes to one part of your code which will keep the services themselves free of role checks and things like that.
I'm trying to setup Facebook Notification API.
I have an APi Controller with RealtimeUpdate() - Get, will be used just for verification of endpoint.
As is written in Fb Docs:
Firstly, Facebook servers will make a single HTTP GET to your callback
URL when you try to add or modify a subscription. A query string will
be appended to your callback URL with the following parameters:
hub.mode - The string "subscribe" is passed in this parameter
hub.challenge - A random string
hub.verify_token - The verify_token value you specified when you created the subscription
From here I have a problem - I have no idea how to handle this dots in query params names. I google a lot, and did not find the solution.
Can somebody please say to me how to get data from this hub.* values?
Thank you!
Update your method signature using the FromUri attributes, like this:
public string Get(
[FromUri(Name="hub.mode")]string mode,
[FromUri(Name="hub.challenge")]string challenge,
[FromUri(Name="hub.verify_token")]string verifyToken
)
{
/* method body */
}
The parameters will be bound from the query string using the specified names.
Slightly different form Steve's answer.
In case you need to have a normal controller instead of an Api one (if you are inheriting from Controller rather tha ApiController), the follow worked for me:
namespace Name
{
public class Hub
{
public string Mode { get; set; }
public string Challenge { get; set; }
// ReSharper disable once InconsistentNaming
public string Verify_Token { get; set; }
}
public class FacebookWebHooksController : Controller
{
[System.Web.Http.HttpGet, ActionName("Callback")]
[AllowAnonymous]
public ContentResult CallbackGet(Hub hub)
{
if (hub.Mode == "subscribe" && hub.Verify_Token == "YOUR_TOKEN")
return Content(hub.Challenge, "text/plain", Encoding.UTF8);
return Content(string.Empty, "text/plain", Encoding.UTF8);
}
}
[HttpPost]
[AllowAnonymous]
public ActionResult Callback()
{
Request.InputStream.Seek(0, SeekOrigin.Begin);
var jsonData = new StreamReader(Request.InputStream).ReadToEnd();
}
}
The Model Binder has some illegal characters, of which I believe '.' is a special character, used primarily to bind complex objects. When all else fails, you can look at Request.QueryString and Request.Form directly, just like in ASP.NET WebForms.
You can also try using a complex object that has a Property named hub with subproperties mode, challenge, and verify_token. This might just do the trick.
I have a .net mvc 4 webapi project that I'm trying to pass an array of an object to a method on my controller.
I've found some examples here on SO that talk about needing to set my object's properties with: param1=whatever¶m2=bling¶m3=blah.
But I don't see how I can pass in a collection using that.
Here is my method signature. Notice I've decorated the argument with the [FromUri] attribute.
public List<PhoneResult> GetPhoneNumbersByNumbers([FromUri] PhoneRequest[] id)
{
List<PhoneResult> prs = new List<PhoneResult>();
foreach (PhoneRequest pr in id)
{
prs.Add(PhoneNumberBL.GetSinglePhoneResult(pr.PhoneNumber, pr.RfiDate, pr.FinDate, pr.State));
}
return prs;
}
here is my simple PhoneRequest object:
public class PhoneRequest
{
public string PhoneNumber { get; set; }
public string RfiDate { get; set; }
public string FinDate { get; set; }
public string State { get; set; }
}
and here's a sample of what I'm using to pass in:
http://localhost:3610/api/phonenumber/getphonenumbersbynumbers/
[{"PhoneNumber":"8016667777","RfiDate":"","FinDate":"2012-02-11","State":"UT"},
{"PhoneNumber":"8018889999","RfiDate":"2012-12-01","FinDate":"","State":"UT"}]
using this comes back with "bad request"
I also tried this
http://localhost:3610/api/phonenumber/getphonenumbersbynumbers?
id=[{"PhoneNumber":"8016667777","RfiDate":"","FinDate":"2012-02-11","State":"UT"},
{"PhoneNumber":"8018889999","RfiDate":"2012-12-01","FinDate":"","State":"UT"}]
which does reach the method, but the array is null.
how can I pass in an array of my PhoneRequest object to my Web API method?
Try passing the PhoneRequest[] from the uri in this format:
http://localhost:3610/api/phonenumber/getphonenumbersbynumbers?
id[0][PhoneNumber]=8016667777&id[0][FinDate]=2012-02-11&id[0][State]=UT&
id[1][PhoneNumber]=8018889999&id[1][RfiDate]=2012-12-01&id[1][State]=UT
I suggest you use POST for this.
As you query string grows, you will run into problems with the maximum length of the URL, which is browser dependent.
If you have a lot of parameters to pass, a POST is perfectly acceptable even if you are really only GETting data. What you will lose, however, is the ability for the user to bookmark a particular page with the query string.
I created a custom model binder, the FieldValueModelBinder class, which can effectively pass any object containing nested array or generic list types of data with query strings having field-name pairs without imbedding any JSON and XML structures. The model binder can resolve all issues discussed above. Since this question was extended by the question ID 19302078, you can see details of my answer in that thread.