How do I pass multiple string parameters into WebAPI - asp.net-web-api

I'm trying to write a simple WebAPI controller to validate a userId and password. Both parameters are strings and it's a bit frustrating that this always seems to be so difficult.
The Controller code looks like:
[Route("api/User/{userId}, {password}"), HttpGet]
public bool IsPasswordCorrect(string userId, string password)
{
UserModel sm = new UserModel(userId);
bool rtn = sm.Password == password;
return rtn;
}
The calling code looks like:
public bool IsPasswordValid(string userId, string password)
{
RestRequest request = new RestRequest("IsPasswordCorrect/{userId}, {password}", Method.GET);
request.AddUrlSegment("userId", userId);
request.AddUrlSegment("password", password);
RestClient restClient = new RestClient(Service.Location);
var response = restClient.Execute<bool>(request);
bool rtn = (bool)response.Data;
return true;
}
I am not able to get the calling code to access the controller code. Thanks

You have defined the route as:
GET {host}/api/User/{userId}, {password}
but looks like you are calling it as:
GET {Service.Location}/IsPasswordCorrect/{userId}, {password}
You should call the Web API resource by the route defined in the attribute not by action name as that would result in 404.

Related

When returning a json, how can you tell using Spring that you want to go to a certain url?

In my code, I am returning a path in String form(/successPassword or /systemError). Basically this tells Spring to go to the path /successPassword or /systemError, /successPassword goes to successPassword.html and /systemError goes to systemError.html. These html files are located in src/main/resources/templates.
#RequestMapping(value = "/user/new_password", method = RequestMethod.POST)
public String createNewPassword(#RequestParam(value = "newpassword", required = false) String password,
#RequestParam(value = "hash") String hash, Model model) {
LOG.info("set new password using hash " + hash);
LOG.info("new password " + password);
boolean hashValid = passwordService.isHashValid(hash);
if (hashValid) {
ValidateMessage message = passwordService.validateNewPassword(password);
if (!message.isError()) {
Account account = passwordService.getAccount(hash);
passwordResetService.saveNewPassword(password, account);
model.addAttribute("account", account);
return "/successPassword";
} else {
LOG.info("Password not complex enough.");
return "/systemError";
}
} else {
LOG.info("Invalid hash.");
return "/systemError";
}
}
Now it looks like I need to return a json object instead. I know that I have to annotate the method with #ResponseBody instead, but my question is how would i package now the object that I will return so that whoever receives it will know to which path it needs to go to? My html codes are using Thymeleaf (no javascript whatsoever).

HttpClient always do Basic Authentication check after provide an authorization header?

The web api control, UserControl, has two methods, RetrieveUserID which needs Basic Authorization check
[HttpGet]
[Route("RetrieveUserID/{strUsername}")]
[Authorize]
public string RetrieveUserID(string strUsername)
{
//retrieve userID and return it
......
return strUserID;
}
Another method, FailAuthenticationReason, is used if fail to retrieve userID, it returns the detail fail info such as wrong username, wrong password, account is locked out, etc. which doesn't need to do any authentication check
[HttpGet]
[Route("FailAuthenticationReason/{strUsername}")]
public string FailAuthenticationReason(string strUsername)
{
//retrieve detail failed reason
......
return strFailedReason;
}//End of
It works well when I use a browser to check them. But when I use it in my app, after I provide the authorization header and fail to retrieve userID because of incorrect username and/or password, it also do the authorization check when it call FailAuthenticationReason
var authData = string.Format("{0}:{1}", entUsername.Text,entPassword.Text);
var authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(authData));
App.httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeaderValue);
var uri = new Uri(string.Format(App.strWebAPIURI + "/RetrieveUserID/{0}", entUsername.Text));
try
{
var response = await App.httpClient.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
......
}
else
{
//Fail to pass authorization
uri = new Uri(string.Format(App.strWebAPIURI + "/FailAuthenticationReason/{0}", entUsername.Text));
response = await App.httpClient.GetAsync(uri);
......
}
How can the program call FailAuthenticationReason without the authorization check?

How to get user context during Web Api calls?

I have an web front end calling an ASP Web Api 2 backend. Authentication is managed with ASP Identity. For some of the controllers I'm creating I need to know the user making the call. I don't want to have to create some weird model to pass in including the user's identity (which I don't even store in the client).
All calls to the API are authorized using a bearer token, my thought is the controller should be able to determine the user context based on this but I do not know how to implement. I have searched but I don't know what I'm searching for exactly and haven't found anything relevant. I'm going for something like...
public async Task<IHttpActionResult> Post(ApplicationIdentity identity, WalkthroughModel data)
Update
I found the below which looked very promising... but the value is always null! My controller inherits from ApiController and has an Authorize header.
var userid = User.Identity.GetUserId();
Update 2
I have also tried all of the solutions in Get the current user, within an ApiController action, without passing the userID as a parameter but none work. No matter what I am getting an Identity that is valid and auth'd, but has a null UserID
Update 3
Here's where I'm at now.
[Authorize]
[Route("Email")]
public async Task<IHttpActionResult> Get()
{
var testa = User.Identity.GetType();
var testb = User.Identity.GetUserId();
var testc = User.Identity.AuthenticationType;
var testd = User.Identity.IsAuthenticated;
return Ok();
}
testa = Name: ClaimsIdentity,
testb = null,
testc = Bearer,
testd = true
The user is obviously authenticated but I am unable to retrieve their userID.
Update 4
I found an answer, but I'm really unhappy with it...
ClaimsIdentity identity = (ClaimsIdentity)User.Identity;
string username = identity.Claims.First().Value;
That gets me the username without any db calls but it seems very janky and a pain to support in the future. Would love if anyone had a better answer.
What if I need to change what claims are issued down the road? Plus any time I actually need the user's id I have to make a db call to convert username to ID
A common approach is to create a base class for your ApiControllers and take advantage of the ApplicationUserManager to retrieve the information you need. With this approach, you can keep the logic for accessing the user's information in one location and reuse it across your controllers.
public class BaseApiController : ApiController
{
private ApplicationUser _member;
public ApplicationUserManager UserManager
{
get { return HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
}
public string UserIdentityId
{
get
{
var user = UserManager.FindByName(User.Identity.Name);
return user.Id;
}
}
public ApplicationUser UserRecord
{
get
{
if (_member != null)
{
return _member ;
}
_member = UserManager.FindByEmail(Thread.CurrentPrincipal.Identity.Name);
return _member ;
}
set { _member = value; }
}
}
I use a custom user authentication (I dont use AspIdentity because my existing user table fields was far different from IdentityUser properties) and create ClaimsIdentity passing my table UserID and UserName to validate my bearer token on API calls.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
User user;
try
{
var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
_service = scope.Resolve<IUserService>();
user = await _service.FindUserAsync(context.UserName);
if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt))
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
catch (Exception ex)
{
context.SetError("invalid_grant", ex.Message);
return;
}
var properties = new Dictionary<string, string>()
{
{ ClaimTypes.NameIdentifier, user.UserID.ToString() },
{ ClaimTypes.Name, context.UserName }
};
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
properties.ToList().ForEach(c => identity.AddClaim(new Claim(c.Key, c.Value)));
var ticket = new AuthenticationTicket(identity, new AuthenticationProperties(properties));
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(identity);
}
And how I use the ClaimsIdentity to retrieve my User table details on User ApiController Details call.
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("Details")]
public async Task<IHttpActionResult> Details()
{
var user = await _service.GetAsync(RequestContext.Principal.Identity.GetUserId<int>());
var basicDetails = Mapper.Map<User, BasicUserModel>(user);
return Ok(basicDetails);
}
Notice the
ClaimTypes.NameIdentifier = GetUserId() and ClaimTypes.Name = GetUserName()

Execute a simple call to a WebAPI Get using RestRequest and a single string parameter

I have the following code in my receiving controller:
[Route("api/StudentUserId/{string}"), HttpGet]
public StudentDto StudentUserId(string userId)
{
StudentModel sm = new StudentModel(userId);
StudentDto dto = sm.ConvertToDto();
return dto;
}
After running this project, I have another project that I use to test the WebAPI controller calls. I use the following code to read a student record form the database using their userId:
protected T SendRequestToReadItemUsingString<T>(string resource, string userId) where T : new()
{
string resourceString = string.Format("{0}/{{userId}}", resource);
RestRequest request = new RestRequest(resourceString, Method.GET);
request.AddUrlSegment("userId", userId);
RestClient restClient = new RestClient(Service.Location);
var response = restClient.Execute<T>(request);
T retVal = response.Data;
return retVal;
}
Comparible code seems to work if I change the userId to an int Id in both the controller and calling code. I can't seem to get it to work with string. If I place a breakpoint in the controller code it never hits it and the calling code just returns a null.
Thanks for your help
Please note that WebApi works based on reflection this means that your curly braces {vars} must match the same name in your methods.
Therefore to match this api/StudentUserId/{string} your method needs to be declare like this:
[Route("api/StudentUserId/{userId}"), HttpGet]
public StudentDto StudentUserId(string userId)
return userId;
}
Where the parameter {string} was replaced by userId.
If you want to read more about this Routing Rules here is similar post on this;
WebApi Routing Configuration

Post Binary array to Web API Controller

I am trying to POST form data which consists of few string variable and binary array.
Below is the Model for the form data.
public class FileModel
{
public string Path { get; set; }
public byte[] File { get; set; }
}
Below is my Web API Controller.
[Route("")]
public IHttpActionResult Post([FromBody]FileModel media)
{
// Can I use ??
byte[] requestFile = media.File;
string requestFilePath = media.Path;
//Process the above variables
return Ok();
}
I would like to know Can I use the following code to de-serialize the following code snippet to to read the values from the JSON payload including the binary data?
byte[] requestFile = media.File;
string requestFilePath = media.Path;
If Yes, Do I need to define any formatter class to get it working?
I normally use POSTMAN to test my RESTful endpoints.
Is it possible to use POSTMAN still to POST binary array? May be not need to write my own client
You'll need to use a serializer to serialize complex objects (multiple fields) as content for a Http Request.
For your code snippet to read the object from the content you can use this:
var requestContent = Request.Content.ReadAsAsync<FileModel>(GetJsonSerializer()).Result;
Here's the serializer boilerplate code.
private JsonMediaTypeFormatter GetJsonSerializer()
{
JsonSerializerSettings settings = new JsonSerializerSettings()
{
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All,
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All
};
return new JsonMediaTypeFormatter() { SerializerSettings = settings };
}
I'm not sure how to use POSTMAN to test this. A simple .net client would be:
var Client = new HttpClient();
Client.BaseAddress = new Uri("localhost"); //whatever your endpoint is
FileModel objectToSend = new FileModel();
var objectContent = new ObjectContent<FileModel>(objectToSend, GetJsonSerializer() );
var response = Client.PostAsync("uri", objectContent);
You are able to use POSTMAN to test binary file input. Selecting the body tab, you can then pick the radio button "binary" and then choose file.

Resources