.Net Framework API Controller won't recognize Route attributes - asp.net-web-api

I have created an API Controller using .Net Framework as follows:
public class ApplicationUsersController : ApiController
{
[Route("api/ApplicationUser/{username}/{password}")]
[ResponseType(typeof(ApplicationUser))]
public IHttpActionResult GetApplicationUser(string username, string password)
{
ApplicationUser user = new ApplicationUser()
//Code to populate user.
return Ok(user);
}
[Route("api/ApplicationUser/{username}")]
[ResponseType(typeof(ApplicationUser))]
public IHttpActionResult GetApplicationUser(string username)
{
ApplicationUser user = new ApplicationUser()
//Code to populate user.
return Ok(user);
}
// PUT: api/ApplicationUsers/5
[Route("api/ApplicationUser/{username}")]
[ResponseType(typeof(void))]
public IHttpActionResult PutApplicationUser(string username, ApplicationUser ApplicationUser)
{
//Code to update user
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/ApplicationUsers
[Route("api/ApplicationUser")]
[ResponseType(typeof(ApplicationUser))]
public IHttpActionResult PostApplicationUser(ApplicationUser ApplicationUser)
{
//Code to create new user
return Ok(ApplicationUser);
// return CreatedAtRoute("api/ApplicationUser/{username}", new { username = ApplicationUser.UserName }, ApplicationUser);
}
// DELETE: api/ApplicationUsers/5
[Route("api/ApplicationUser/{username}")]
[ResponseType(typeof(ApplicationUser))]
public IHttpActionResult DeleteApplicationUser(string username)
{
//Code to populate user then delete the record.
return Ok(user);
}
}
When I make a Get call to api/ApplicationUser/{username}/{password}, it works fine. If I make a Post call to api/ApplicationUser, it works fine. If I make a Get, Put or Delete call to api/ApplicationUser/{username}, I get a "not found" error. Is there something else I need to do to make it recognize the route?
Thanks,
Jim
**** Update ****
I have discovered that it will recognize the route as long as the username doesn't end with .something such as .com. The thing is, I am using email addresses as the username. Is there a rule somewhere that a REST url can't end with .somthing? Is there a way around this?

The problem was the format of the parameters. Apparently a url can't end with a .com or other domain suffix. What I did was to convert the parameters to Base64. I created these two extension functions.
public static string ToBase64(this string value)
{
try
{
byte[] bytes = Encoding.UTF8.GetBytes(value);
return Convert.ToBase64String(bytes);
}
catch (Exception)
{
return value;
}
}
public static string FromBase64(this string value)
{
try
{
byte[] bytes = Convert.FromBase64String(value);
return Encoding.UTF8.GetString(bytes);
}
catch(Exception)
{
return value;
}
}
In the controller, I did something like:
[Route("api/ApplicationUser/{username}")]
[ResponseType(typeof(ApplicationUser))]
public IHttpActionResult GetApplicationUser(string username)
{
username = username.FromBase64();
ApplicationUser user = new ApplicationUser()
//Code to populate user.
return Ok(user);
}
In the client, I did something like:
async Task<ApplicationUser> IApplicationUserService.GetApplicationUser(string username)
{
username = username.ToBase64();
ApplicationUser ret = null;
var response = await _httpClient.GetAsync($"api/ApplicationUser/{username}");
if (response.IsSuccessStatusCode)
{
ret = await JsonSerializer.DeserializeAsync<ApplicationUser>
(await response.Content.ReadAsStreamAsync(), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
}
return ret; ;
}
Cheers,
Jim

Related

DBContext error when attempting to edit/update

When performing inline edit in a jquery/jqgrid table, I am seeing the ID (PK) and the column values come across to my public IActionResult Edit URL with a 200 response. However I am then encountering an error where my DBContext does not exist, however it's the same dbcontext I used to retrieve the data. Also, not able to return a string message? The code up until my Edit works, what am I doing wrong?
namespace MyProject.Controllers
{
public class CheckRequestController : Controller
{
private readonly MyDBContext _context;
public CheckRequestController(MyDBContext context)
{
_context = context;
}
public IActionResult Index()
{
return View();
}
public IActionResult LoadData()
{
try
{
var fileData = (from tempFile in _context.CheckRequest
select tempFile);
var data = fileData.ToList();
return Json(data);
}
catch (Exception)
{
throw;
}
}
// I'm trying to run an update to the row in SQL Server. I see my ID and the values come across 200 status but can't update.. Also can't return string from IAction
// Also can't return string from IActionResult or ActionResult. How to return message if failure
public IActionResult Edit(CheckRequest checkrequests)
{
try
{
if (ModelState.IsValid)
{
using (MyDBContext db = new MyDBContext())
{
db.Entry(checkrequests).State = EntityState.Modified;
db.SaveChanges();
}
}
}
catch (Exception ex)
{
//msg = "Error occured:" + ex.Message;
var msg = new string[] { "Error occured:" + ex.Message };
}
return msg;
}
}
}
I removed the readonly and was able to trace the server and see the request coming across; can't believe my eyes didn't catch that. Still curious how to capture and return an error message that's useful though on the return in the IActionResult. Right now I changed it to return Ok() so it's not really useful if an error is to occur.

aspnet web api how to validate using modelstate in multipart form data?

I am new for using asp net web api.
I want to create UploadDocument feature in my web api.
so, I create this way.
From Client
api.post('vendor/UploadDocument', formData, { headers: { 'Content-Type': 'multipart/form-data' } })
Controller
public class VendorController : ApiController {
[HttpPost]
public HttpResponseMessage UploadDocument()
{
try
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var request = HttpContext.Current.Request;
var model = new UploadDocumentViewModel();
model.field1 = request.Form["field1"];
model.field2 = request.Form["field2"];
model.Document = request.Files["Document"];
if (ModelState.IsValid)
{
return Request.CreateResponse(HttpStatusCode.OK);
}
else //ModelState is not valid
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
catch (Exception exception)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
}
}
Model
public class UploadDocumentViewModel
{
[Required]
public string field1 { get; set; }
[Required]
public int field2 { get; set; }
[Required]
public HttpPostedFile Document { get; set; }
}
My problem is, in controller ModelState always empty.
I have tried to add code
Validate(model);
if (ModelState.IsValid)...
but it didn't work too.
can someone have any idea for validating model data annotation in multipart form data ?
try clear model state before validate
ModelState.Clear();
this.Validate(model);
if (ModelState.IsValid) {
}
Check this answer : Custom DataAnnotation
Your method should look like this:
public class VendorController : ApiController {
[HttpPost]
public IHttpActionResult UploadDocument(UploadDocumentViewModel viewModel)
{
try
{
if (!Request.Content.IsMimeMultipartContent())
return StatusCode(HttpStatusCode.UnsupportedMediaType);
if (viewNodel == null)
return BadRequest("Model is empty");
var field1 = viewModel.field1;
var field2 = viewModel.field2;
var documents = viewModel.document;
if (ModelState.IsValid)
{
return Ok();
}
else
{
return BadRequest(ModelState);
}
}
catch (Exception exception)
{
return InternalServerError(exception);
}
}
}
I prefer to passing some of those validations in action filters, to make your methods cleaner (try/catch, modelstate).
If you will have some problems with model binding, you can implement your custom model binder.

How to write into session with web api?

I am writing an authentication code. I am authenticating against the web server. currently my code take the username and password from xcode and send it over to the web service via the URL which then returns a json string that I am reading in xcode. When the connection is succefull I want to create a session and in xcode i want to read that session.
Web Api:
public class SessionController : ApiController
{
public bool loggedin = false;
public class MyHttpControllerHandler: HttpControllerHandler, IRequiresSessionState
{
public MyHttpControllerHandler(RouteData routeData): base(routeData)
{ }
}
public class MyHttpControllerRouteHandler : HttpControllerRouteHandler
{
protected override IHttpHandler GetHttpHandler(
RequestContext requestContext)
{
return new MyHttpControllerHandler(requestContext.RouteData);
}
}
public void Authenticate(string txtLoginId, string txtPassword)
{
Subs objSub = SubService.GetSubs(txtLoginId.Trim(), txtPassword.Trim());
if (objSub != null)
{
loggedin = true;
}
else
loggedin = false;
}
public string Get(string user, string pass)
{
byte[] data = Convert.FromBase64String(pass);
string password = Encoding.UTF8.GetString(data);
Authenticate(user, password);
if(loggedin == true)
{
var session = HttpContext.Current.Session;
session["Time"] = DateTime.Now;
return "Session Time: " + session["Time"] + user;
}else
return "Session is not availabe " + user;
}
}
it returns the following error on this line,
session["Time"] = DateTime.Now;
ExceptionMessage":"Object reference not set to an instance of an object."

HOw to redirect sessiont timeout page when do AJAX call

In MVC application, I have made most of the CRUD operations call using AJAX.
Problem is , when session gets timeout then, its unable to redirect to session time out page.
Below is the related code which works fine when there is no AJAX call.
[AttributeUsage(AttributeTargets.Class)] //| AttributeTargets.Method
public class ControllerLogAndAccessFilter : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
\\check if session is null then redirect to session time out page.
}
}
For login, i just check against the databsae, there is nothing for memebrship provider.
[HttpPost]
public JsonResult Login(string username, string password, bool RememberMe)
{
try
{
UserDTO accDTO = new UserDTO ()
{
UsernAme = username,
Password = DataEncryption.EncryptPassword(password)
};
UserDTO AccDTO = _iAccount.UserAuthentication(accDTO);
if (AccDTO != null)
{
Session["UserId"] = 1;
Session["userdto_Session"] = AccDTO;
// Remember me
HttpCookie myCookie = new HttpCookie("appCookie");
//chkRememberMe.Checked;
if (RememberMe)
{
myCookie.Values.Add("username", username);
myCookie.Values.Add("password", password);
myCookie.Expires = DateTime.Now.AddMinutes(20);
}
else
{
myCookie.Values.Add("username", string.Empty);
myCookie.Values.Add("password", string.Empty);
myCookie.Expires = DateTime.Now.AddMinutes(5);
}
Response.Cookies.Add(myCookie);
// Remember me
return Json(AccDTO.SID, JsonRequestBehavior.AllowGet);
}
else
{
return Json(null);
}
}
catch (Exception ex)
{
}
return null;
}
AJAX Call is made for login and all CRUD operation are using $.AJAX({...});.
Ajax requests should be handled at client side itself, Try this
Attribute:
[AttributeUsage(AttributeTargets.Class)] //| AttributeTargets.Method
public class ControllerLogAndAccessFilter : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
//TO HANDLE AJAX REQUESTS
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
//If session is null
filterContext.Result = new JsonResult
{
Data = new
{
// put a message which sentto the client
message = "Session Time out"
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}else{
//
}
}
}
In your javascript
$.ajax(function(){
url:"",
success: function (result) {
if(result.message == "Session Time out"){
//Session timed out handle it
//window.location.href = session timeout url
}
},
});

How to customize authentication to my own set of tables in asp.net web api 2?

In the default AccountController created I see
public AccountController()
: this(Startup.UserManagerFactory(), Startup.OAuthOptions.AccessTokenFormat)
{
}
In Startup.Auth.cs I see
UserManagerFactory = () =>
new UserManager<IdentityUser>(new UserStore<IdentityUser>());
Seems like the implementation of UserStore comes from Microsoft.AspNet.Identity.EntityFramework.
So, to customize the authentication do I have to implement my own version of UserStore like
class MYSTUFFUserStore<IdentityUser> : UserStore<IdentityUser>
{
}
and override the methods and then do this in Startup.Auth.cs
UserManagerFactory = () =>
new UserManager<IdentityUser>(new MYSTUFFUserStore<IdentityUser>());
I am looking for a correct way to customize the authentication.
Assuming your table is called AppUser, convert your own AppUser domain object to IUser(using Microsoft.AspNet.Identity) like this
using Microsoft.AspNet.Identity;
public class AppUser : IUser
{
//Existing database fields
public long AppUserId { get; set; }
public string AppUserName { get; set; }
public string AppPassword { get; set; }
public AppUser()
{
this.Id = Guid.NewGuid().ToString();
}
[Ignore]
public virtual string Id { get; set; }
[Ignore]
public string UserName
{
get
{
return AppUserName;
}
set
{
AppUserName = value;
}
}
}
Implement the UserStore object like this
using Microsoft.AspNet.Identity;
public class UserStoreService
: IUserStore<AppUser>, IUserPasswordStore<AppUser>
{
CompanyDbContext context = new CompanyDbContext();
public Task CreateAsync(AppUser user)
{
throw new NotImplementedException();
}
public Task DeleteAsync(AppUser user)
{
throw new NotImplementedException();
}
public Task<AppUser> FindByIdAsync(string userId)
{
throw new NotImplementedException();
}
public Task<AppUser> FindByNameAsync(string userName)
{
Task<AppUser> task = context.AppUsers.Where(
apu => apu.AppUserName == userName)
.FirstOrDefaultAsync();
return task;
}
public Task UpdateAsync(AppUser user)
{
throw new NotImplementedException();
}
public void Dispose()
{
context.Dispose();
}
public Task<string> GetPasswordHashAsync(AppUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.AppPassword);
}
public Task<bool> HasPasswordAsync(AppUser user)
{
return Task.FromResult(user.AppPassword != null);
}
public Task SetPasswordHashAsync(AppUser user, string passwordHash)
{
throw new NotImplementedException();
}
}
If you have your own custom password hashing you will also need to implement IPasswordHasher. Below is an example where there is no hashing of the password(Oh no!)
using Microsoft.AspNet.Identity;
public class MyPasswordHasher : IPasswordHasher
{
public string HashPassword(string password)
{
return password;
}
public PasswordVerificationResult VerifyHashedPassword
(string hashedPassword, string providedPassword)
{
if (hashedPassword == HashPassword(providedPassword))
return PasswordVerificationResult.Success;
else
return PasswordVerificationResult.Failed;
}
}
In Startup.Auth.cs replace
UserManagerFactory = () =>
new UserManager<IdentityUser>(new UserStore<IdentityUser>());
with
UserManagerFactory = () =>
new UserManager<AppUser>(new UserStoreService()) { PasswordHasher = new MyPasswordHasher() };
In ApplicationOAuthProvider.cs, replace IdentityUser with AppUser
In AccountController.cs, replace IdentityUser with AppUser and delete all the external authentication methods like GetManageInfo and RegisterExternal etc.

Resources