Using the Web API in .net 5 and conforming an api in rest gives me some challenges about how much to split up the methods in different controllers and the naming conventions.
I have read that if I have users in my system and doing a rest architecture, my controller would be named UserController and method for getting a user would be:
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
For getting a list of users it would be:
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
Updating would be:
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
Delete would have a [HttpDelete], and so on. But what about the special cases?
What if I wanted GetUserByUsername? Would it then be in the same UserController and just be the following, or would it break the REST pattern?:
[HttpGet("{username}")]
public Task GetByUsername(string username)
{
}
What if I needed a call to the api get some data to populate the "createuser page", lets say I need the roles that the user could be created as and some other information and would like to do it in one call. Could I then just create a "InitCreateUser" in the UserController and not break the REST pattern?
[HttpPost]
public Task InitCreateUser()
{
}
What if I needed Login and Logout methods, would it be AutenticationController and just have the two methods: (It's just so far from the other pattern when its not called just Get() Post() and so on)
[HttpPost]
public Task Login(LoginRequest request)
{
}
[HttpPost]
public Task Logout(LogoutRequest request)
{
}
Yes you would put it in the same controller as it is still dealing with the user, and it is good habit to keep functions involving a specific entity in the same location.
But that obviously has the issue of having different endpoints such as Id and Username. A simple way to do this is to indicate which you want to use:
[HttpGet("Id/{id}")]
public string Get([FromRoute] int id) {...}
-> api/User/Id/293
[HttpGet("Username/{username}")]
public string Get([FromRoute] string username) {...}
-> api/User/Username/Tom
Another way is to have a base request and have other functions be offshoots of it
[HttpGet("{id}")]
public string Get([FromRoute] int id) {...}
-> api/User/293
[HttpGet("Username/{username}")]
public string Get([FromRoute] string username) {...}
-> api/User/Username/Tom
[HttpGet("Customer/{custNo}")]
public string Get([FromRoute] string custNo) {...}
-> api/User/Customer/5DtU22D
But lets say you have the Id of the user and you wish to do other functions like get relevant data from the user to display, but not all of it. Or You want to check all the relating data towards a specific funciton,
then you can do something like this:
[HttpGet("{id}/Permissions")]
public PermissionModel GetUserPermission([FromRoute] int id) {...}
[HttpPut("{id}/Permissions")]
public bool UpdateUserPermission([FromRoute] int id, [FromBody] PermissionModel permission) {...}
Or even further derived functionalities
[HttpGet("{id}/Account")]
public string GetUserAccount([FromRoute] int id) {...}
[HttpGet("{id}/Account/Funds")]
public double GetUserAccountTotal([FromRoute] int id) {...}
if you are accessing list properties, for example the user has many accounts for example, you can add a secondary key:
[HttpGet("{id}/Accounts")]
public IEnumerable<string> GetUserAccount([FromRoute] int id) {...}
[HttpGet("{id}/Accounts/{accountId}/Funds")]
public double GetUserAccountTotal([FromRoute] int id, [FromRoute] int accountId) {...}
Related
I want to allow users of an API to send GET request that will run multiple queries on db and then return the result.
I have a query model like this
public class QueryModel
{
public int A {get;set;}
public int B {get;set;}
public int C {get;set;}
}
I have a controller with a Get method like this -
public async Task<IActionResult> Get(List<QueryModel> queryModels)
{
foreach(var queryModel in queryModels)
{
// some logic to search the db
}
// combine the results and return
}
This is not working for me, but I don't know if my query string is wrong or the method is wrong.
I have tried a number of variations of
?model={[{1,2,3},{1,2,3}]}
But they do not work.
You can use a construction like this:
\Get?model[0].A=1&model[0].B=2&model[0].C=3&model[1].A=4&model[1].B=5&model[1].C=6
Almost forgot, add FromUri:
public async Task<IActionResult> Get([FromUri] List<QueryModel> model)
{
...
}
Please let me know if this works for you.
Is it possible to add columns to the AspNetUserLogins table, or subclass the IdentityUserLogin class, such that the Identity Framework will use that class properly?
This is an answer but I'm sure it's not going to end up the best one:
It can be done, but it's ugly.
First, you'll want to make a class of all the generics you're about to use, just to make your life easier. Those are:
[Table("AspNetUserRoles")]
public class StandardUserRole : IdentityUserRole<string>
[Table("AspNetRoles")]
public class StandardRole : IdentityRole<string, StandardUserRole>
[Table("AspNetUserLogins")]
public class LoginIdentity : IdentityUserLogin
(The above superclasses can be found in Microsoft.AspNet.Identity.EntityFramework).
This is going to make the following generic definitions shorter, and harder to get into a place where they won't compile due to clerical errors.
While you're here may as well add these to the DbContext, which normally does not leave them available to you:
public DbSet<LoginIdentity> LoginIdentities { get; set; }
public DbSet<StandardUserRole> UserRoles { get; set; }
Now, here comes the crazy:
public class Db :
// Replace this with a custom implementation
//IdentityDbContext<Visitor>,
IdentityDbContext<Visitor, StandardRole, string, LoginIdentity,
StandardUserRole, IdentityUserClaim>,
And, Visitor is going to need its own adjustment to match this declaration:
public class Visitor : IdentityUser<string, LoginIdentity, StandardUserRole,
IdentityUserClaim>
That satisfies the Models (which btw, are best to have in their own Project for Migrations performance reasons). But, you've still got all the Identity/OWIN stuff to deal with.
By default you're provided with an ApplicationUserManager that involves a UserStore. It normally inherits from UserManager, but that's going to be too restrictive now - you need to slightly expand it:
public class VisitorManager : UserManager<Visitor, string>
{
public VisitorManager(IUserStore<Visitor, string> store)
: base(store)
{
}
public static VisitorManager Create(
IdentityFactoryOptions<VisitorManager> options,
IOwinContext context)
{
var manager = new VisitorManager(new UserStore<Visitor,
StandardRole, string, LoginIdentity, StandardUserRole,
IdentityUserClaim>(context.Get<Db>()));
I warned you about crazy. SignInManager:
public class SignInManager : SignInManager<Visitor, string>
{
public SignInManager(VisitorManager userManager,
IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(
Visitor user)
{
return user.GenerateUserIdentityAsync((VisitorManager)UserManager);
}
public static SignInManager Create(
IdentityFactoryOptions<SignInManager> options, IOwinContext context)
{
return new SignInManager(context.GetUserManager<VisitorManager>(),
context.Authentication);
}
}
That should get you through most of the dirty work. Not easy. But, having done that, you've got a working implementation where you can add extra fields to the Logins table! You can now extend the OWIN Auth stuff to provide events, and listen for the creation of new Logins. You can then respond to those by adding that extra info.
In our case, the goal was to have multiple Logins from multiple OpenId/OAuth Providers (Google, Facebook, etc) across multiple email addresses, on a single User/Visitor account. The framework does support that, but, it doesn't make a record of what Email is associated with what Login row, which is important when merging more Logins with a given account.
[Table("AspNetUserLogins")]
public class LoginIdentity : IdentityUserLogin
{
/// <summary>
/// The email address associated with this identity at this provider
/// </summary>
[MaxLength(300)]
public string Email { get; set; }
}
There's more you'll need to do to get the whole thing working, but it should be relatively obvious from the above starting point - with one exception, which I'll point out here.
By migrating from UserManager<TVisitor> to UserManager<TVisitor, string>, you quietly lose the ID-generation functionality built-in to the former. You'll need to emulate it yourself. As another gotcha, along the way you'll most likely implement Visitor as IUser<string>. Doing so will prevent you from setting the Id property, because it's read-only (no setter). You can avoid that with a second interface:
public interface IVisitor
{
string Id { get; set; }
string Uid { get; set; }
string UserName { get; set; }
string Email { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
ICollection<StandardUserRole> Roles { get; }
ICollection<LoginIdentity> Logins { get; }
}
With that in place you can set Id safely (even in an abstracted class):
public override Task<IdentityResult> CreateAsync(Visitor user)
{
var guid = Guid.NewGuid();
string id = guid.ToString();
((IVisitor)user).Id = id;
return base.CreateAsync(user);
}
Remember to do same for CreateAsync(Visitor user, string password). Otherwise created users explode with DbEntityValidationException complaining Id is a required field.
How can I pass more than 1 parameters as part of query string to my asp.net web api 2.
This is my asp.net web api 2 method, I am not able to figure out that how can I decorate this method so that it accepts the id and a complex type which is CustomerRequest, I want to use Url something like
http://localhost/api/Customer/?Mobile0012565987&Email=abcxyz.com&IsEmailVerified=true
[ResponseType(typeof(Customer))]
public IHttpActionResult GetCustomer(long id, [FromUri]CustomerRequest request)
{
var customer = db.Customers.Find(request.CustomerId);
if (customer == null)
{
return NotFound();
}
return Ok(customer);
}
This is CustomerRequest class
public class CustomerRequest
{
public string Mobile { get; set; }
public string Email { get; set; }
public Nullable<bool> IsEmailVerified { get; set; }
}
Otherwise pleaase guide me if there is a better way to do it.
Thanks
Based on your code, you need to pass 'id' as well, like this:
http://localhost/api/Customer/?id=12345&Mobile=0012565987&Email=abcxyz.com&IsEmailVerified=true
if you want to make 'id' optional, you can make your method signature look like this:
public IHttpActionResult GetCustomer([FromUri]CustomerRequest request, long id = 0)
this will set id to 0 by default, if you dont pass it in the URL. So you will be able to access your URL like you originally did:
http://localhost/api/Customer/?Mobile=0012565987&Email=abcxyz.com&IsEmailVerified=true
I am trying to make a RESTful application in Java using Spring boot by following the tutorial here. I want to modify it so that I can extract an identifier from the URL and use it to serve requests.
So http://localhost:8080/members/<memberId> should serve me a JSON object with information about the member whose ID is <memberId>. I don't know how to
Map all http://localhost:8080/members/* to a single controller.
Extract the from the URL.
Should the logic of extracting the memberId and using it be part of the controller or a separate class, as per the MVC architecture?
I am new to Spring/Spring-boot/MVC. It is quite confusing to get started with. So please bear with my newbie questions.
Map all http://localhost:8080/members/* to a single controller.
You can use a placeholder in a request mapping to so it'll handle multiple URLs. For example:
#RequestMapping("/members/{id}")
Extract the id from the URL
You can have the value of a placeholder injected into your controller method using the #PathVariable annotation with a value that matches the name of the placeholder, "id" in this case:
#RequestMapping("/members/{id}")
public Member getMember(#PathVariable("id") long id) {
// Look up and return the member with the matching id
}
Should the logic of extracting the memberId and using it be part of the controller or a separate class, as per the MVC architecture?
You should let Spring MVC extract the member id from the URL as shown above. As for using it, you'll probably pass the URL to some sort of repository or service class that offers a findById method.
As you can see in the code below, service for customer are in one controller to get one and to add new customer.
So, you will have 2 services:
http://localhost:8080/customer/
http://localhost:8080/customer/{id}
#RestController("customer")
public class SampleController {
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Customer greetings(#PathVariable("id") Long id) {
Customer customer = new Customer();
customer.setName("Eddu");
customer.setLastname("Melendez");
return customer;
}
#RequestMapping(value = "/{id}", method = RequestMethod.POST)
public void add(#RequestBody Customer customer) {
}
class Customer implements Serializable {
private String name;
private String lastname;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getLastname() {
return lastname;
}
}
}
I'm trying to roll out a strategy pattern with entity framework and the repository pattern using a simple example such as User and Post in which a user has many posts.
From this answer here, I have the following domain:
public interface IUser {
public Guid UserId { get; set; }
public string UserName { get; set; }
public IEnumerable<Post> Posts { get; set; }
}
Add interfaces to support the roles in which you will use the user.
public interface IAddPostsToUser : IUser {
public void AddPost(Post post);
}
Now my repository looks like this:
public interface IUserRepository {
User Get<TRole>(Guid userId) where TRole : IUser;
}
Strategy (Where I'm stuck). What do I do with this code? Can I have an example of how to implement this, where do I put this?
public interface IFetchingStrategy<TRole> {
TRole Fetch(Guid id, IRepository<TRole> role)
}
My basic problem was what was asked in this question. I'd like to be able to get Users without posts and users with posts using the strategy pattern.
If we talk about strategy pattern then IFetchingStrategy must be passed to IUserRepository so I think you should modify Get operation:
public interface IUserRepository
{
User Get<TRole>(Guid userId, IFetchingStrategy<TRole> strategy) where TRole : IUser;
}
But I'm not sure how to implement such interfaces with EF.
If we return to your former question, it can also be accomplished this way:
public interface IUserRepository
{
User Get(Guid userId, IEnumerable<Expression<Func<User,object>>> eagerLoading);
}
public class UserRepository : IUserRepository
{
public User Get(Guid userId, IEnumerable<Expression<Func<User,object>>> eagerLoading)
{
ObjectQuery<User> query = GetBaseQuery(); // get query somehow, for example from ObjectSet<User>
if (eagerLoading != null)
{
foreach(var expression in eagerLoading)
{
// This is not supported out of the box. You need this:
// http://msmvps.com/blogs/matthieu/archive/2008/06/06/entity-framework-include-with-func-next.aspx
query = query.Include(expression);
}
}
return query.SingleOrDefault(u => u.Id == userId);
}
}
You will use the method this way:
User userWithoutPosts = repository.Get(guid, null);
User userWithPosts = repository.Get(guid, new List<Expression<Func<User,object>>>
{
u => u.Posts
});
But I guess that this implementation works only for first level of navigation properties.
An answer in the following post uses strategy pattern to manipulate a query.
https://codereview.stackexchange.com/questions/3560/is-there-a-better-way-to-do-dynamic-filtering-and-sorting-with-entity-framework