Web Api add a location to a user - c#

I want to add a location to a user (My userDto has a list of location he wants to visit)
[HttpPut]
[Route("{id:guid}/location/")]
public IHttpActionResult AddLocationToUser(Guid idUser , LocationDto location)
{
_userLogic.AddLocationToUser(idUser, location);
return Ok();
}
it's ok to make a Put ? (because I have a function who just add a location to a list in user and then I want to update ) but how my route should look like ???
It's ok [Route("{id:guid}/location/")] ??
I pass the userId from session , but It's ok to send the whole location in PUT??

Since it is adding/creating a record, you want to use HttpPost. For LocationDto, you want to use [FromBody].
For example,
[HttpPost]
[Route("{id:guid}/location")]
public IHttpActionResult AddLocationToUser(Guid id, [FromBody] LocationDto location)
{
_userLogic.AddLocationToUser(id, location);
return Ok();
}

I second #Win's suggestion of using HttpPost. If you are using Web Api 2, then the following is another alternative.
Controller:
public class LocationController : ApiController
{
UserLogic _userLogic;
public LocationController()
{
_userLogic = new UserLogic();
}
public void PostLocationToUser(LocationViewModel locationViewModel)
{
_userLogic.AddLocationToUser(locationViewModel.UserId, locationViewModel.Location);
}
}
View Model:
public class LocationViewModel
{
public Guid UserId { get; set; }
public Location Location { get; set; }
}
public class Location
{
public string Latitude { get; set; }
public string Longitude { get; set; }
}
JSON:
var input = {
UserId: "11111111-1111-1111-1111-111111111111",
Location: {
Latitude: "anotherLatitude",
Longitude: "anotherLongitude"
}
};
I used the default Web Api routing, the url is "/api/Location", and the PostLocationToUser returns a status code of 204 (no content).

Related

Web API HTTPGet for multiple attributes?

We have a Web API written in DotNet Core 3.1.402 (I am new to DotNet Core and WebAPI).
We use SqlKata for Database processing.
We have an Account model that has AccountID, AccountName, AccountNumber, etc.
We would like to get an Account by different attributes, for ex: by AccountID, by AccountName, by AccountNumber.
How can we do that so that we don't need a separate HttpGet for each attribute (so we don't have to repeat the same code for different attributes) ?
This is our HttpGet in the AccountsController to get the account by AccountID
public class AccountsController : ControllerBase
{
private readonly IAccountRepository _accountRepository;
[HttpGet("{AccountID}")]
public Account GetAccount(int AccountID)
{
var result = _accountRepository.GetAccount(AccountID);
return result;
}
This is the code in the AccountRepository.cs
public Account GetAccount(int accountID)
{
var result = _db.Query("MyAccountTable").Where("AccountID", accountID).FirstOrDefault<Account>();
return result;
}
This is the Account class
namespace MyApi.Models
{
public class Account
{
public string AccountID { get; set; }
public string AccountName { get; set; }
public string AccountNumber { get; set; }
// other attributes
}
}
Thank you.
Doing it with GET can be a pain, there are ways to pass on the path/query arrays and complex objects but are ugly, the best you can do is to use POST instead of GET and pass an object with the filters that you want.
//In the controller...
[HttpPost]
public Account GetAccount([FromBody]Filter[] DesiredFilters)
{
var result = _accountRepository.GetAccount(DesiredFilters);
return result;
}
//Somewhere else, in a shared model...
public class Filter
{
public string PropertyName { get; set; }
public string Value { get; set; }
}
//In the repository...
public Account GetAccount(Filter[] Filters)
{
var query = _db.Query("MyAccountTable");
foreach(var filter in Filters)
query = query.Where(filter.PropertyName, filter.Value);
return query.FirstOrDefault<Account>();
}
Now you can send a JSON array on the request body with any filters that you want, per example:
[
{ "PropertyName": "AccountID", "Value": "3" },
{ "PropertyName": "AccountName", "Value": "Whatever" }
]

How to get and post common data to each request made to the web api

I have created a simple webapi service with few get/post methods, these methods are having some input parameters that client is passing while making call to it, other than these parameter I have some common parameters that has to pass in each request made to the web api, currently I added in every web api method as input parameter that is passing by client along with other input parameters. I am looking for a way where I don'n need to add these common parameters on every webapi method, I want to get these common parameters commonly under webapi.
This is my sample api controller
public class MessageController : ApiController
{
//companyID is a common parameter that is required to pass every web api method
public IHttpActionResult GetMessage(string messageCode, int companyID)
{
Message msg = null;
MesssageManager msgManager = null;
try
{
if(string.IsNullOrEmpty(messageCode))
{
throw new Exception("Plase pass the messageCode in order to get the message.");
}
msgManager = new MesssageManager();
List<Message> messages = msgManager.GetMessages(companyID);
msg = messages.FirstOrDefault(o => o.Code.Equals(messageCode, StringComparison.InvariantCultureIgnoreCase));
return Ok(msg);
}
catch (Exception)
{
throw;
}
finally
{
msgManager = null;
}
}
public IHttpActionResult GetWarningMessage(string warningCode, int companyID)
{
//doing actual stuff to get the data
}
public IHttpActionResult GetMthod1(string param1, int companyID)
{
//doing actual stuff to get the data
}
public IHttpActionResult GetMthod2(string param1, int companyID)
{
//doing actual stuff to get the data
}
[HttpPost]
public IHttpActionResult SaveMessage(string message, int companyID)
{
//doing actual stuff to get the data
}
}
In above controller "companyID" is a common parameter that has to pass in each request.
Please suggest me implementation in web api to get the common parameters, and how to pass it from client using HttpClient.
If the companyID is some kind of indentification/authentication parameter you could add the companyId to the request headers. Implement an authenticationfilter and grab the companyId from the headers. However, you still need some kind of short term persisting mechanism (session, cache, scoped DI container etc.) where the authentication filter would store the parameter and the controller method would get the parameter from.
At the end you need to pass the parameter from the client to the server each time it is required. You need to figure out if it's less hassle to put it into the headers or pass it as a parameter to the method. If the companyId varies from request to request I'd add it to each method. If the companyId is "static" for at least a duration of a session then I'd put it into the headers and would try to make sure, that the client automatically adds the appropriate companyId to the request headers (i.e. like you would handle user tokens).
Please refer below line
https://www.codeproject.com/Tips/574576/How-to-implement-a-custom-IPrincipal-in-ASP-NET-MV
We can add additional attribute in CustomPrincipalSerializedModel like below
public interface ICustomPrincipal : System.Security.Principal.IPrincipal
{
string FirstName { get; set; }
string LastName { get; set; }
int CompanyId { get;set; }
}
public class CustomPrincipal : ICustomPrincipal
{
public IIdentity Identity { get; private set; }
public CustomPrincipal(string username)
{
this.Identity = new GenericIdentity(username);
}
public bool IsInRole(string role)
{
return Identity != null && Identity.IsAuthenticated &&
!string.IsNullOrWhiteSpace(role) && Roles.IsUserInRole(Identity.Name, role);
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get { return FirstName + " " + LastName; } }
public int CompanyId { get;set; }
}
public class CustomPrincipalSerializedModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int CompanyId { get;set; }
}
https://www.codeproject.com/Tips/574576/How-to-implement-a-custom-IPrincipal-in-ASP-NET-MV
We can get from header or cookies or use Custom identity principals

Exclude controller name from routing in net core asp

I am using asp net core mvc and trying to develop a RESTful app. Let's say I have models like this:
internal class Album
{
public long Id { get; set; }
public string Name { get; set; }
public long ExecutorId { get; set; }
public virtual Executor Executor { get; set; }
}
internal class Executor
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Album> Albums { get; set; }
}
And I have 2 controllers, that I wrote like this:
[ApiVersion("1")]
[Route("api/v{api-version:apiVersion}/[controller]")]
public class ExecutorsController : Controller
{
[HttpGet("{id}", Name = "GetExecutor")]
public IActionResult GetById(long id)
{
//some code
}
[HttpGet(Name = "GetExecutors")]
public IActionResult GetAll()
{
//some code
}
}
[ApiVersion("1")]
[Route("api/v{api-version:apiVersion}/[controller]")]
public class AlbumController : Controller
{
[HttpGet("{id}", Name = "GetAlbum")]
public IActionResult GetById(long id)
{
//some code
}
[HttpGet(Name = "GetAlbums")]
public IActionResult GetAll()
{
//some code
}
}
I can call http://localhost:48234/api/v1/Album/1 and get album by Id,
I can call http://localhost:48234/api/v1/Album and get all albums. The same thing works with executors. So, what I want to do, is to be able to get albums by executorId and that my route would look like
http://localhost:48234/api/v1/executors/1/albums, which will return all albums for executor with Id = 1. The problem is that I would like to put this action into AlbumsController:
[HttpGet]
[Route("executors/{executorId}/albums")]
public IActionResult GetAlbumsByExecutorId(long executorId)
{
return new ObjectResult(_service.GetAlbumsByExecutorId(executorId));
}
This code works just fine, but it puts ControllerName (Albums) at the beginning. How can I remove ControllerName from my route? Or maybe I shouldn't do this and just put this action to ExecutorsController? I decided to do it like this, because if action returns albums, it should be placed in AlbumsController. Am I wrong?
Just remove [controller] from route path in controller and move it to the methods.
Something like this:
[ApiVersion("1")]
[Route("api/v{api-version:apiVersion}/")]
public class AlbumController : Controller
{
[HttpGet("album/{id}", Name = "GetAlbum")]
public IActionResult GetById(long id)
{
//some code
}
[HttpGet("album", Name = "GetAlbums")]
public IActionResult GetAll()
{
//some code
}
[HttpGet]
[Route("executors/{executorId}/albums")]
public IActionResult GetAlbumsByExecutorId(long executorId)
{
return new ObjectResult(_service.GetAlbumsByExecutorId(executorId));
}
}
If you want to get albums, you should place action method in AlbumController.
I'd like to recommend you this way:
[HttpGet(Name = 'GetAlbumsByExecutorId')]
[Route("{executorId}/albums")]
public IActionResult GetAlbumsByExecutorId(long executorId)
{
return new ObjectResult(_service.GetAlbumsByExecutorId(executorId));
}

How to send an object to MVC controller using AngularJS

I have an object, I need to send it to my MVC controller;
object:
params = {Id: 1, UserAge: 32 }
// Angular $htttp
function test() {
var request = $http.post('/user/updateuser/', params);
return request;
}
c# - controller
public class User
{
public int Id { get; set; }
public int UserAge { get; set; }
}
[HttpPost]
public void UpdateUser(User user)
{
//user Id and user age is always zero.
}
UserId and UserAge is always 0 i'm not sure what i'm missing.
Try to add [FromBody] attribute before the input parameter:
public void UpdateUser([FromBody]User user) {}

How to pass data from different controllers

I am trying to pass a value which is stored in one controller to another, code is below:
Charities Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Donate([Bind(Include = "ID,DisplayName,Date,Amount,Comment")] Charity charity)
{
if (ModelState.IsValid)
{
if (!string.IsNullOrEmpty(charity.Comment))
{
var comment = charity.Comment.ToLower().Replace("hot", "###").Replace("cold", "###").Replace("Slow", "###").Replace("enjoy", "###").Replace("BAD", "###");
charity.Comment = comment; //Replaces textx from model variable - comment
charity.TaxBonus = 0.20 * charity.Amount;
}
if (string.IsNullOrEmpty(charity.DisplayName))
{
charity.DisplayName = "Annonymus"; //If user doesnt enter name then Annonymus
}
db.Donations.Add(charity);
db.SaveChanges();
TempData["Name"] = charity.DisplayName;
TempData["Amount"] = charity.Amount;
TempData["Comment"] = charity.Comment;
return RedirectToAction("../Payments/Payment", "Charities", new { id = charity.Amount });
}
return View(charity);
}
Charities Class
public class Charity
{
public int ID { get; set; }
[RegularExpression(#"^[a-zA-Z]+$", ErrorMessage = "Use letters only please")]
public string DisplayName { get; set; }
[DataType(DataType.Currency)]
[Range(2, Int32.MaxValue, ErrorMessage = "Atleast £2.00 or a whole number please")]
public int Amount { get; set; }
[DataType(DataType.Currency)]
public Double TaxBonus { get; set; }
public String Comment { get; set; }
public static object Information { get; internal set; }
}
Payment Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Payment([Bind(Include = "ID,CardName,CardNumber,ValidFrom,Expires,CardSecurityCode,EmailAddress,ConfrimEmailAddress,Address,City,Country,PostCode")] Payment payment)
{
if (ModelState.IsValid)
{
db.Payments.Add(payment);
db.SaveChanges();
TempData["Name"] = charity.DisplayName
TempData["Amount"];
TempData["Comment"];
TempData["Name"] = payment.CardName;
TempData["Email"] = payment.EmailAddress;
return RedirectToAction("Confirmation", "Payments", new { id = payment.ID });
}
return View(payment);
}
Payment Class
public class Payment
{
public int ID { get; set; }
}
public class CharityDBContext : DbContext //controls information in database
{
public DbSet<Charity> Donations { get; set; } //creates a donation database
}
public class PaymentDBContext : DbContext //controls information in database
{
public DbSet<Payment> Payments { get; set; } //creates a donation database
public System.Data.Entity.DbSet<CharitySite.Models.Charity> Charities { get; set; }
}
}
I am trying to get this from the Charities Controller
TempData["Name"] = charity.DisplayName;
To display in Payment controller
TempData["Name"] = charity.DisplayName;
Right now theres a squigly red line under "charity" in the payment controller with the message - doesnt exist in current context. I just wanted to know if it is possible to pass data from different controllers using temp data.
First of all, the line return RedirectToAction in your Donate method is going to send a 302 response to your browser which will issue a GET request to the url in the location header of the response, which in this case is Payment/Payment. But your Payment method is marked with HttpPost. Are you sure you want to send a second GET request to a method marked with HttpPost to save some part of the data(Payment) you want to save ?
I think you should save your charity and payment info in the same action method( Create a PaymentCharity view model and use that instead of using the Bind attribute and the entity classes created by EF to transfer data from your view to action method). Also, insteaof using TempData to pass data, What you should do is, get the unique id of the Payment record you saved, pass that in querystring to the second action method and in that using the unique payment id,read the payment record again and use that.
So in your Donate method,
public ActionResult Donate(PaymentCharirtVm model)
{
var charity = new Charity { DisplayName =model.Amount,Comment =model.Comment};
var payment = new Payment ();
//set the properties of payment here
db.Donations.Add(charity);
db.SaveChanges();
//now save Payment
db.Payment.Add(payment);
db.SaveChanges();
return RedirectToAction("Confirmation","Payment", new { id=payment.Id });
}
I have never tried to do it with tempdata. Have never needed to. I recomend you add the string with the charity name to the view model used by the payment controller.
Please correct code as below to retrieve value from TempData.
charity.DisplayName= TempData["Name"]
As already answered here, most answer are correct. It would be better to use routeValues in the RedirectToAction method to pass value from one action to another action in the same controller or different controller.
return RedirectToAction("actionName", "anotherControllerName", new { id = charity.DisplayName });
In the another controller, data can be retrieved by
string displayName = RouteData.Values["id"].ToString();
Here, in the routeValues, you can pass the whole Charity object as well and do some casting in the another controller to get the object properly in the that case when you need more than one properties to be sent:
return RedirectToAction("actionName", "anotherControllerName", new { id = charity });
And get it by:
Charity chatiry = (Charity)RouteData.Values["id"];
I hope that you will get some idea from this.

Categories