Since the two following controller's actions are possible, I am wondering if there is any difference in terms of performance (or best practices) between them.
[HttpGet("example1")]
public ActionResult<User> GetExample1()
{
var user = new User { UserName = "Example 1 user" };
var model = new Model<User>(user);
return Ok(model);
}
[HttpGet("example2")]
public ActionResult<Model<User>> GetExample2()
{
var user = new User { UserName = "Example 2 user" };
var model = new Model<User>(user);
return Ok(model);
}
Please notice that the only difference between the two is the returned type of the generic parameter of ActionResult (User vs Model<User>). Is there any important internal differences when the ActionResult object is formatting out the result?.
User and Model code for this example:
public class User
{
public string UserName { get; set; }
}
public class Model<T>
{
public T Data { get; set; }
public Model(T data)
{
Data = data;
}
}
Related
I'm trying to write this controller that accepts a POST request.
I need this controller to add a new book, and also add that new books bookId to another object called a StoreList.
So I am trying to pass in the new bookList, and the storeList that needs the bookId added to it.
// POST: api/BookList
[HttpPost]
public async Task<ActionResult<BookList>> PostBookList(BookList bookList, StoreList storeList)
{
_context.BookList.Add(bookList);
await _context.SaveChangesAsync();
_context.Entry(storeList).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!StoreListExists(storeId))
{
return NotFound();
}
else
{
throw;
}
}
return CreatedAtAction("GetBookList", new { id = bookList.BookId }, bookList);
}
Here is my API endpoint:
https://localhost:44362/api/BookList/
And these are the two objects I'm passing in the BODY of the request (the new bookList and the existing storeList):
{
"bookId": "bc381612-c63b-4438-b35b-161a3a568fc7",
"bookTitle": "Is this a test 2?"
},
{
"storeId": "0001f801-6909-4b6e-8652-e1b49745280f",
"bookId": "bc381612-c63b-4438-b35b-161a3a568fc7"
}
But whenever I try to 'hit' that endpoint, I get this error:
System.InvalidOperationException HResult=0x80131509 Message=Action
'DocumentStorageAPI.Controllers.Book.BookListController.PostBookList
(DocumentStorageAPI)' has more than one parameter that was
specified or inferred as bound from request body. Only one
parameter per action may be bound from body. Inspect the following
parameters, and use 'FromQueryAttribute' to specify bound from query,
'FromRouteAttribute' to specify bound from route, and
'FromBodyAttribute' for parameters to be bound from body: BookList
bookList StoreList storeList
How can I get my controller to allow me to add a new bookList and update the needed storeList?
Thanks!
The body of the request should be just one object and the PostBookList method must have only one parameter (with the [FromBody] attribute). If you need both classes to use within the method create a new class like this:
public class PostBookListRequest
{
public BookList BookList { get; set; }
public StoreList StoreList { get; set; }
}
change the PostBookList method to
public async Task<ActionResult<BookList>> PostBookList([FromBody]PostBookListRequest request)
{
// Your logic here
}
And in the BODY of the request do
{
"bookList": {
"bookId": "bc381612-c63b-4438-b35b-161a3a568fc7",
"bookTitle": "Is this a test 2?"
},
"storeList": {
"storeId": "0001f801-6909-4b6e-8652-e1b49745280f",
"bookId": "bc381612-c63b-4438-b35b-161a3a568fc7"
}
}
You can use body like this:
{
"bookId": "bc381612-c63b-4438-b35b-161a3a568fc7",
"title": "Is this a test 2?",
"storeId" "0001f801-6909-4b6e-8652-e1b49745280f"
}
In Controller you need additional class BookListBinding with this 3 fields, which you will use for create you 2 objects, for example.
[HttpPost]
public async Task<ActionResult> PostBookList(BookListBinding binding)
{
var bookList = new BookList
{
Id = binding.BookId,
Title = binding.Title
});
var storeList = new StoreList
{
Id = binding.StoreId,
BookId = binding.BookId
}
// you work with _context
return CreatedAtAction("GetBookList", new { id = binding.BookId }, bookList);
}
Why you need change StoreList? What is it?
You have to create a new model like so-
public class AddBookToStoreModel
{
public Book BookToAdd { get; set; }
public StoreList BookStoreList { get; set; }
}
You have to add the same to the controller definition, like this-
[HttpPost]
public async Task<ActionResult<BookStore>> PostBookList(AddBookToStoreModel model)
Also you need to create the individual Models for Book and StoreList like below-
public class Book
{
public Guid BookId { get; set; }
public string BookTitle { get; set; }
}
public class StoreList
{
public Guid StoreId { get; set; }
public Guid BookId { get; set; }
}
The Json that you post to the controller will look like below-
{
"BookToAdd": {
"bookId": "bc381612-c63b-4438-b35b-161a3a568fc7",
"bookTitle": "Is this a test 2?"
},
"BookStoreList": {
"storeId": "0001f801-6909-4b6e-8652-e1b49745280f",
"bookId": "bc381612-c63b-4438-b35b-161a3a568fc7"
}
}
I hope this works for you.
In our Asp.Net Core (2.2) MVC project we had to use an existing database (including all user & role related tables) from our previous Asp.Net Web app project.
Retrieving user data in asp.net web app (and having it available throughout the website) was preatty simple: upon login fill a custom user class/object with all the properties you need, save it as a Session variable and you call it wherever you need it (without going to the database).
This seems to me a lot harder to achieve in Asp.Net Core. What I have so far is:
ApplicationUser class:
public class ApplicationUser : IIdentity
{
public int Id { get; set; }
public Uporabnik Uporabnik { get; set; }
public string AuthenticationType { get; set; }
public bool IsAuthenticated { get; set; }
public string Name { get; set; }
}
Login form:
public IActionResult Prijava(PrijavaModel model)
{
// check user credentials
//
// ... validation code here ...
//
if (uporabnik != null)
{
//Create the identity for the user
var identity = new ClaimsIdentity(new[] {
new Claim("Email", model.Email),
new Claim("Id", uporabnik.IdWebUser.ToString()),
new Claim("Name", uporabnik.ImeInPriimek),
new Claim(ClaimTypes.Name, uporabnik.ImeInPriimek),
new Claim(ClaimTypes.PrimarySid, uporabnik.IdWebUser.ToString())
}, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
var login = HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
return RedirectToAction("Index", "Home");
}
return View();
}
Now to retrieve the data in a controller I have to do something like this:
// id
int idUser = int.Parse(#User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
// or email
string email = #User.Claims.FirstOrDefault(x => x.Type == "Email").Value;
Well, this all works fine, but it's definitely not practical. To access any other user data I can go to the database (by "ID") and retrieve it, but I don't think this is the right way to do it!?!
Can I expand the identity class in such a way that I can set the extra properties I need at login time and retrive in a fashion similar to something like this:
var property1 = #User.Property1;
var property2 = #User.Property2;
// or
var property1 = #User.MyExtraProperties.Property1;
var property2 = #User.MyExtraProperties.Property2;
Is it possible (and also keeping it simple)?
EDIT: since there are no answers/suggestions, can I do the same thing with a different approach?
Look like you only want to call your properties in a better way?
public class ApplicationUser : IdentityUser
{
public string CustomName { get; set; }
}
Assuming you have done adding your extra properties, you could create an extension method for your properties, so you can later call them like User.Identity.GetCustomName().
namespace Project.Extensions
{
public static class IdentityExtensions
{
public static string GetCustomName(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("CustomName");
return (claim != null) ? claim.Value : string.Empty;
}
}
}
Note that I didn't include the part where you add the claims, because you already have it. In this case, you should have CustomName claim.
Also, #Dementic is right about the session. If a user is removed/disabled, he would still have access to. So, having a db call each time you need to fetch information is correct.
I'm attempting to return a true or false (in JSON) along with my model data. Code builds and runs fine but I only get a return on my model data.
What I've tried from reading other answers:
public IQueryable<Book> GetBooks()
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
return db.Books;
}
As you can probably easily see, I don't have the greatest idea of what I'm doing, but through the infinite wisom of this community I hope to learn another feat.
I would create a view model and returned that from the controller
public class BooksVm
{
public IQueryable<Book> Books { get; set; }
public bool BooleanValue { get; set; }
}
and then in your controller
public IHttpActionResult GetBooks()
{
var booksVm = new BooksVm() { Books = db.Books, BooleanValue = true };
return Ok(booksVm);
}
I need to make a query against a document collection that matches several properties.
(Cross post from the mailing list: https://groups.google.com/forum/?fromgroups=#!topic/ravendb/r5f1zr2jd_o)
Here is the document:
public class SessionToken
{
[JsonProperty("jti")]
public string Id { get; set; }
[JsonProperty("aud")]
public Uri Audience { get; set; }
[JsonProperty("sub")]
public string Subject { get; set; }
[JsonProperty("claims")]
public Dictionary<string, string> Claims { get; set; }
}
And here is the test:
[TestFixture]
public class RavenDbTests
{
private IDocumentStore documentStore;
[SetUp]
public void SetUp()
{
this.documentStore = new EmbeddableDocumentStore() { RunInMemory = true };
this.documentStore.Initialize();
}
[Test]
public async void FirstOrDefault_WhenSessionTokenExists_ShouldReturnSessionToken()
{
var c = new SessionToken()
{
Audience = new Uri("http://localhost"),
Subject = "NUnit",
Claims = new Dictionary<string, string>()
{
{ ClaimTypes.System, "NUnit" }
}
};
using (var session = this.documentStore.OpenAsyncSession())
{
await session.StoreAsync(c);
await session.SaveChangesAsync();
// Check if the token exists in the database without using Where clause
var allTokens = await session.Query<SessionToken>().ToListAsync();
Assert.That(allTokens.Any(x => x.Subject == "NUnit" && x.Audience == new Uri("http://localhost")));
// Try getting token back with Where clause
var token = await session.Query<SessionToken>().Customize(x => x.WaitForNonStaleResults()).Where(x => x.Subject == "NUnit" && x.Audience == new Uri("http://localhost")).ToListAsync();
Assert.IsNotNullOrEmpty(token.First().Id);
}
}
}
The last Assert is the one that is failing.
I must admit Im not sure whether this is a bug or a failure on my part.
As far as I understand, this is supposed to work.
PS. I´ve tried with a standalone document store as well as embedded without running in memory, but with same result.
You are getting stale results. In a unit test, you need to allow time for indexing to occur.
Add .Customize(x=> x.WaitForNonStaleResults()) to your queries and the test should pass.
Also, I think you left the Id property off your question when you cut/paste because it doesn't compile as-is.
UPDATE
Per discussion in comments, the issue was that you were applying the [JsonProperty] attribute to the Id property. Since the Id property represents the document key, and is not serialized as part of the JSON document, you can't apply the [JsonProperty] attribute to it.
This is probably quite straight forward for some, however I'm a bit confused and can't find a decent example. Say I'm using view models and my POST action takes in that view model. Typically I would do something along the following lines:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
User user = Mapper.Map<UserViewModel, User>(uvm);
_repository.Update(user);
return RedirectToAction("Index");
}
Although this isn't the full picture. The mapping would work fine, however if I were to just update what I've mapped then it'd get rid of valuable data in the database because of course in this case I'm not updating the password or other details.
My repository looks something like this:
public void Update(User user)
{
User u = Session.QueryOver<User>().Where(x => x.UserName == user.UserName).SingleOrDefault();
if (u == null)
throw new Exception("User not found");
u.Forename = user.Forename;
u.Surname = user.Surname;
u.EmailAddress = user.EmailAddress;
}
[I'm using NHibernate so it'll save the object back to the DB once the session is closed (after the request has finished) automatically for me.]
So my question is, in my repository should I load the "User" entity, then update the values I want, and then save it back, or is there another method to do this? The reason I ask is because it seems a bit... "manual" if you see what I mean? Perhaps it is correct, but I just wanted to see opinions of those with more experience in this area.
Cheers
I use the following approach:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
User user = _userRepository.FindById(uvm.Id);
user.Forename = uvm.Forename;
user.Surname = uvm.Surname;
user.EmailAddress = uvm.EmailAddress;
_userRepository.Update(user);
return RedirectToAction("Index");
}
UPDATE:
To address the comments about AutoMapper here's how to proceed:
Let's take for example the following classes:
public class UserViewModel
{
public string Forename { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
}
public class User
{
public string Forename { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
}
We don't want to modify the user password in the UI. So we express our intention to AutoMapper:
Mapper
.CreateMap<UserViewModel, User>()
.ForMember(dest => dest.Password, opt => opt.Ignore());
and then:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
// Fetch the original model we would like to update
User user = _userRepository.FindById(uvm.Id);
Mapper.Map(uvm, user);
// At this stage the user model will have its
// Forename, Surname and EmailAddress properties
// updated from the view model and its Password property
// will remain the one we got from the repository
_userRepository.Update(user);
return RedirectToAction("Index");
}
UPDATE 2:
To address the question in the comments about configuring AutoMapper I usually use Profiles:
public class UsersProfile : Profile
{
protected override void Configure()
{
Mapper
.CreateMap<UserViewModel, User>()
.ForMember(dest => dest.Password, opt => opt.Ignore());
Mapper
.CreateMap<User, UserViewModel>();
}
}
and then have a registry class which registers all the mappers:
public class MappingsRegistry
{
public static void Configure()
{
Mapper.AddProfile(new UsersProfile());
Mapper.AddProfile(new SomeOtherProfile());
...
}
}
which is called in Application_Start:
MappingsRegistry.Configure();
Finally my controllers have a reference to the mapping engine:
public class UsersController : Controller
{
private readonly IUsersRepository _repository;
private readonly IMappingEngine _mappingEngine;
public ContratsFCController(IUsersRepository repository, IMappingEngine mapperEngine)
{
_repository = repository;
_mapperEngine = mapperEngine;
}
[AutoMap(typeof(User), typeof(UserViewModel))]
public ActionResult Update(int id)
{
var user = _repository.FindById(id);
return View(user);
}
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
if (!ModelState.IsValid)
{
return View(uvm);
}
var user = _repository.FindById(uvm.Id);
_mapperEngine.Map(uvm, user);
_repository.Update(user);
return RedirectToAction("Index");
}
}
Now all that's left is to instruct your DI framework to pass the Mapper.Engine property to the constructor and in your unit tests obviously substitute them with an appropriate mock.