how can I customize the name of a request [HttpGet] in c # - c#

I have created the following web service and can access it by:
https://localhost:44311/valores/1
but I want to access it with a url like:
https://localhost:44311/usuario/1
using usuario in the url
[HttpGet("{id:int}",Name ="usuario")]
public ActionResult<Usuario> Get(int id)
{
using (var db = new prueba2Context())
{
var usuario = db.Usuario.Where(x => x.Id == id).FirstOrDefault();
if (usuario == null)
{
return NotFound();
}
return Ok(usuario);
}
}
I am new to c#, I appreciate if you indicate what I am doing wrong and how to correct it.
This is the structure of my folder.

It looks like you are using ASP.NET Core. A typical endpoint will be set up like this:
[ApiController, Route("api/[controller]")]
public class ComputationController : ControllerBase
{
// ...
[HttpPost, Route("beginComputation")]
[ProducesResponseType(StatusCodes.Status202Accepted, Type = typeof(JobCreatedModel))]
public async Task<IActionResult> BeginComputation([FromBody] JobParametersModel obj)
{
return Accepted(
await _queuedBackgroundService.PostWorkItemAsync(obj).ConfigureAwait(false));
}
[HttpGet, Route("computationStatus/{jobId}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(JobModel))]
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(string))]
public async Task<IActionResult> GetComputationResultAsync(string jobId)
{
var job = await _computationJobStatusService.GetJobAsync(jobId).ConfigureAwait(false);
if(job != null)
{
return Ok(job);
}
return NotFound($"Job with ID `{jobId}` not found");
}
// ...
}
The [ProducesResponseType] attribute is for documentation frameworks such as Swagger.
I always use the [Route] attribute to define the endpoint path.
In your case, I would set it up as so:
[HttpGet, Route("usuario/{id}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Usuario))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetUser(int id)
{
using (var db = new prueba2Context())
{
var usuario = await db.Usuario.Where(x => x.Id == id).FirstOrDefault();
if (usuario == null)
{
return NotFound();
}
return Ok(usuario);
}
}

Is more common use "Route"
like that
`[Route("usuario")]
public ActionResult<Usuario> Get(int id)
{
using (var db = new prueba2Context())
{
var usuario = db.Usuario.Where(x => x.Id == id).FirstOrDefault();
if (usuario == null)
{
return NotFound();
}
return Ok(usuario);
}
}
`

Related

C# Controller behavior changes based on variable name

I don't know enough about C#, .NET, or the MVC pattern to know exactly what is relevant to include here, but I'm pulling my hair out with a very simple change I'm working on.
I have a controller with a Search action (method?) that looks like:
public string Search(int id)
{
return $"The id was {id}";
}
and when I hit the route I get the expected response, e.g.
$ curl https://localhost:7180/Players/Search/1
The id was 1
but when I change the variable name from id to anything else, the behavior changes and the value goes to 0 for some reason.
public string Search(int thing)
{
return $"The thing was {thing}";
}
$ curl https://localhost:7180/Players/Search/1
The thing was 0
I thought maybe it had to do with the Model itself, because the model code at least has an Id attribute
public class Player
{
public int Id { get; set; }
public string? Name { get; set; }
}
but renaming that variable to name (which seems analogous) also doesn't help.
So what concept am I missing here? Why can't I just rename that variable to whatever I want? Thanks in advance!
(I don't know how better to communicate all the different aspects of the code, so here is a link to the line in question, inside the project)
By default MVC registers (see either Program or Startup) next default route, so it can bind id parameter of method as positional part of path:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
You can change the parameter name for example using attribute routing:
[Route("[controller]/search/{thing}")]
public string Search(int thing)
{
return $"The thing was {thing}";
}
Or using HTTP verb templates:
[HttpGet("[controller]/search/{thing}")]
public string Search(int thing)
{
return $"The thing was {thing}";
}
Check the linked docs for other options/details.
I believe this has to do with the way you've defined your route in Program.cs:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
You'll want to add a new definition like this:
app.MapControllerRoute(
name: "default",
pattern: "Players/Search/{thing?}");
or, you could use attribute-based route definitions to move the route pattern definition closer to the actual code. See the MSFT docs for details. Basically, add app.MapControllers(); to Program.cs, then for your individual routes, do something like this:
[Route("Players/Search/{thing}")]
public string Search(int thing)
{
return $"The thing was {thing}";
}
You can decorate the method and define the parameter.
// GET api/values/5
[HttpGet("{id}")]
public virtual async Task<ActionResult<IEntity>> Get(string id)
{
var entity = await Repository.GetEntity(x => x.Id == id);
if (entity == null) return NotFound();
return Ok(entity);
}
Here is another example of an API Controller
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly GameDataContext context;
public UserController(GameDataContext context)
{
this.context = context;
}
// GET: /<controller>/
// GET api/user
[HttpGet]
public ActionResult<IEnumerable<User>> Get()
{
return context.Users.Where(x => x.IsDeleted == false).ToArray();
}
// GET api/user/5
[HttpGet("{id}", Name = "GetUser")]
public ActionResult<User> Get(int id)
{
var user = context.Users.FirstOrDefault(x => x.Id == id && x.IsDeleted == false);
if (user != null)
{
return user;
}
return NotFound();
}
// GET api/user/username/5
[HttpGet("username/{id}", Name = "GetUserByGameId")]
public ActionResult<User> GetByUser(string gameId)
{
var user = context.Users.FirstOrDefault(x => x.UserGameId.Equals(gameId) && x.IsDeleted == false);
if (user != null)
{
return user;
}
return NotFound();
}
// POST api/user
[HttpPost]
public async Task<ActionResult> Post([FromBody] User value)
{
if (value == null)
{
return BadRequest();
}
var user = context.Users.FirstOrDefault(x => x.UserGameId.Equals(value.UserGameId) && x.IsDeleted == false);
if (user != null)
{
return BadRequest("User already exists!");
}
context.Users.Add(value);
await context.SaveChangesAsync();
return CreatedAtRoute("GetUser", new { id = value.Id }, value);
}
// PUT api/user/steamId
[HttpPut("{gameId}")]
public async Task<ActionResult> Put(string gameId, [FromBody] User value)
{
if (value == null || !value.UserGameId.Equals(gameId))
{
return BadRequest();
}
var user = context.Users.FirstOrDefault(x => x.UserGameId.Equals(gameId) && x.IsDeleted == false);
if (user == null)
{
return NotFound();
}
user.UserGameId = value.UserGameId;
user.FirstName = value.FirstName;
user.MiddleName = value.MiddleName;
user.LastName = value.LastName;
user.Email = value.Email;
context.Users.Update(user);
await context.SaveChangesAsync();
return new NoContentResult();
}
// DELETE api/user/steamId
[HttpDelete("{gameId}")]
public async Task<ActionResult> Delete(string gameId)
{
if (string.IsNullOrEmpty(gameId))
{
return BadRequest();
}
var user = context.Users.FirstOrDefault(x => x.UserGameId.Equals(gameId) && x.IsDeleted == false);
if (user == null)
{
return NotFound();
}
user.IsDeleted = true;
context.Users.Update(user);
var scores = context.Scores.Where(x => x.UserId == user.Id);
foreach (var score in scores)
{
score.IsDeleted = true;
context.Scores.Update(score);
}
await context.SaveChangesAsync();
return new NoContentResult();
}
}

Extension method on Entity Framework 6 in ASP.NET MVC 5 on the .NET Framework 4

I'm customizing an old web application, built on ASP.NET MVC 5, .NET 4.6, Entity Framework 6.
I used Entity Framework to built it with a database-first approach. I have not used DDD / Repository / Services layer (it's a simple architecture).
I need to do this:
I don't want to massively update database records
Every time that I create/edit/list a PERSON_ENTITY item or dataset, I run a method on it
For example, turn to upper case FIRSTNAME and LASTNAME properties/fields or round DAYS_FROM_LAST_LOGIN
I don't want duplicate code in create/edit/list action of PERSON_ENTITY controller
namespace Webapplication4.Controllers
{
[Authorize]
public class PersonsController : Controller
{
private CS_Webapplication4_Entities db = new CS_Webapplication4_Entities();
public ActionResult Index()
{
var myDataset = db.PERSON_ENTITY ;
//----------------------------------** 1° point **
foreach(PERSON_ENTITY myPerson in myDataset)
{
myPerson.Firstname = Utils.Upperize(myPerson.Firstname);
myPerson.Lastname = Utils.Upperize(myPerson.Lastname);
}
return View(myDataset.ToList());
}
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
PERSON_ENTITY myPerson = db.PERSON_ENTITY.Find(id);
if (myPerson == null)
{
return HttpNotFound();
}
////---------------------------------- 2° point
myPerson.Firstname = Utils.Upperize(myPerson.Firstname);
myPerson.Lastname = Utils.Upperize(myPerson.Lastname);
return View(myPerson);
}
public ActionResult Create()
{
//...
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "...")] PERSON_ENTITY myPerson)
{
if (ModelState.IsValid)
{
//3° point
myPerson.Firstname = Utils.Upperize(myPerson.Firstname);
myPerson.Lastname = Utils.Upperize(myPerson.Lastname);
db.PERSON_ENTITY.Add(myPerson);
db.SaveChanges();
return RedirectToAction("Index");
}
//...
return View(myPerson);
}
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
PERSON_ENTITY myPerson = db.PERSON_ENTITY.Find(id);
if (myPerson == null)
{
return HttpNotFound();
}
//...
//4° point
myPerson.Firstname = Utils.Upperize(myPerson.Firstname);
myPerson.Lastname = Utils.Upperize(myPerson.Lastname);
return View(myPerson);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "...")] PERSON_ENTITY myPerson)
{
//5° point
myPerson.Firstname = Utils.Upperize(myPerson.Firstname);
myPerson.Lastname = Utils.Upperize(myPerson.Lastname);
if (ModelState.IsValid)
{
db.Entry(myPerson).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
//...
return View(myPerson);
}
}
}
I read that is possible to extend Entity Framework, by adding some extension methods. If that's possible, I can write something like this:
public static AppIdentityDbContext MyExtension()
{
var MyCustomizedMethod = new SOMETHING() {
if EntityType.Equals(PERSON_ENTITY)
{
PERSON_ENTITY.Firstname = Utils.Upperize(myPerson.Firstname);
PERSON_ENTITY.Lastname = Utils.Upperize(myPerson.Lastname);
}
};
return new AppIdentityDbContext().AddSomething(MyCustomizedMethod);;
}
Please, can someone help me to do this?
Is it possible to extend Entity Framework as outlined above?
Thanks to all
Well, you've almost done it. First deifine a static class for the context's extensions and add your new custom extension method
public static class AppIdentityDbContextExtensions()
{
public static bool AddSomething(this AppIdentityDbContext appIdentityDbContext )
{
<your-code-here>
};
}
And then call the new extension method like this
return new AppIdentityDbContex().AddSomething();

Attribute Routing In Asp.net Core Web api

I try to start asp.net core web api routing attribute as default route but when I access routing with parameter, I could not get any response
[Route("api/[controller]")]
[ApiController]
public class WarehousesController : ControllerBase
{
private readonly ApplicationDbContext _context;
public WarehousesController(ApplicationDbContext context)
{
_context = context;
}
//http://localhost:2394/api/Warehouses/Project/1 (Not working)
[HttpGet("Project/{projectId}")]
public async Task<IActionResult> GetWarehouseByProjectId([FromRoute] int projectId)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var warehouse = await _context.warehouses.Include(x => x.Projects).Where(y => y.Projects.FirstOrDefault().ProjectId == projectId).ToListAsync();
if (warehouse == null)
{
return NotFound();
}
return Ok(warehouse);
}
}
Try with this one .This works fine for me
[HttpGet("Project/{projectId}")]
public async Task<IActionResult> GetWarehouseByProjectId([FromRoute] int projectId)
{
if (!ModelState.IsValid)
{
return BadRequest("Invalid state");
}
var warehouse =await _context.warehouses.FindAsync(projectId);
if (warehouse == null)
{
return NotFound("not found");
}
return Ok(warehouse);
}

How to correctly use optional route parameters in this controller to simplify it?

I have a controller that is made with ASP.NET and I really want to simplify that thing with quick view:
// REST representation of Storage
// There is always at least two options to view them
// Data as is or Quick view at metrics averages
[Route("metrics")]
public class MetricsController : Controller
{
// Get raw Storage object
[HttpGet]
public IActionResult GetStorageView()
{
// TODO: do not use in production
WSManModule.HyperVMetric.test(false);
//
var response = MetricsService.Instance.GetRawMetrics();
if (response == null)
{
return NotFound();
}
if (Request.QueryString.Value == "?q=quick")
{
return Ok(new StorageQuickView(response));
}
return Ok(response);
}
// Get metrics for specific device
[HttpGet("{deviceName}")]
public IActionResult GetDeviceView(string deviceName)
{
var response = MetricsService.Instance.GetDeviceMetrics(deviceName);
if (response == null)
{
return NotFound();
}
if (Request.QueryString.Value == "?q=quick")
{
return Ok(new DeviceQuickView(response));
}
return Ok(response);
}
// Get metrics for specific component within the device
[HttpGet("{deviceName}/{componentName}")]
public IActionResult GetComponentView(string deviceName, string componentName)
{
var response = MetricsService.Instance.GetComponentMetrics(deviceName, componentName);
if (response == null)
{
return NotFound();
}
if (Request.QueryString.Value == "?q=quick")
{
return Ok(new ComponentQuickView(response));
}
return Ok(response);
}
}
now it does have a lot of repetition and I don't like it.
Is there any way to do it right with optional parameters like {quick?} or something similar?
Simply: I want to perform different operations if we have /quick at the end of the route or no.
Just accept the q parameter with your actions:
// Get raw Storage object
[HttpGet]
public IActionResult GetStorageView(string q)
{
// TODO: do not use in production
WSManModule.HyperVMetric.test(false);
//
var response = MetricsService.Instance.GetRawMetrics();
if (response == null)
{
return NotFound();
}
if (q == "quick")
{
return Ok(new StorageQuickView(response));
}
return Ok(response);
}
// Get metrics for specific device
[HttpGet("{deviceName}")]
public IActionResult GetDeviceView(string deviceName, string q)
{
var response = MetricsService.Instance.GetDeviceMetrics(deviceName);
if (response == null)
{
return NotFound();
}
if (q == "quick")
{
return Ok(new DeviceQuickView(response));
}
return Ok(response);
}
The action method parameters are not just derived from routes. The values come from Value Providers, and one of the default providers parses the query string. So, you only need to add the query string value to your action method parameters rather than parsing or comparing the query string manually.
you can create a private method like this:
private IAction ProcessResponse<T>(IMyResponseType response)
{
if(response == null)
{
return NotFound();
}
if (Request.QueryString.Value == "?q=quick")
{
var okInstance = (T) Activator.CreateInstance(typeof (T), response);
return Ok(okInstance);
}
return Ok(response);
}
and use it like this:
// Get metrics for specific component within the device
[HttpGet("{deviceName}/{componentName}")]
public IActionResult GetComponentView(string deviceName, string componentName)
{
var response = MetricsService.Instance.GetComponentMetrics(deviceName, componentName);
return ProcessResponse<ComponentQuickView>(response);
}
// Get raw Storage object
[HttpGet]
public IActionResult GetStorageView()
{
// TODO: do not use in production
WSManModule.HyperVMetric.test(false);
//
var response = MetricsService.Instance.GetRawMetrics();
return ProcessResponse<StorageQuickView>(response);
}

How to create ASP.NET Core Web API controller method with similar but different parameter

I created a simple Web API with ASP.NET Core. I have the following API:
GET /api/messages - get all messages
GET /api/messages/{id} - get a message by id
POST /api/messages - add a new message
PUT /api/messages/{id} - update an existing message
DELETE /api/messages/{id} - delete a message
Now, I want another API to get all messages by message owner's name.
What I tried:
I tried to create this API, but it doesn't work because it conflicts with GET /api/messages/{id}:
GET /api/messages/{name} <- (doesn't work due to conflicting API)
// GET: api/messages/{name}
[HttpGet("{name}")]
public IEnumerable<Message> GetMessagesByName(string name)
{
return _repository.GetMessages().Where(m => m.Owner == name);
}
Here is my Message model Message.cs:
public class Message
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string Owner { get; set; }
public string Text { get; set; }
}
Here is my Messages controller MessagesController.cs:
[Route("api/[controller]")]
public class MessagesController : Controller
{
private readonly IMessageRepository _repository;
public MessagesController(IMessageRepository repository)
{
_repository = repository;
}
// GET: api/messages
[HttpGet]
public IEnumerable<Message> Get()
{
return _repository.GetMessages();
}
// GET api/messages/{id}
[HttpGet("{id}", Name = "GetMessage")]
public IActionResult GetById(long id)
{
var message = _repository.GetMessage(id);
if (message == null)
{
return NotFound();
}
return new ObjectResult(message);
}
// POST api/messages
[HttpPost]
public IActionResult Post([FromBody]Message message)
{
if (message == null)
{
return BadRequest();
}
_repository.AddMessage(message);
return CreatedAtRoute("GetMessage", new { id = message.Id }, message);
}
// PUT api/messages/{id}
[HttpPut("{id}")]
public IActionResult Put(long id, [FromBody]Message message)
{
if (message == null || message.Id != id)
{
return BadRequest();
}
var messageToUpdate = _repository.GetMessage(id);
if (messageToUpdate == null)
{
return NotFound();
}
messageToUpdate.Owner = message.Owner;
messageToUpdate.Text = message.Text;
_repository.UpdateMessage(messageToUpdate);
return new NoContentResult();
}
// DELETE api/messages/{id}
[HttpDelete("{id}")]
public IActionResult Delete(long id)
{
var message = _repository.GetMessage(id);
if (message == null)
{
return NotFound();
}
_repository.RemoveMessage(id);
return new NoContentResult();
}
}
Question:
How can I create an API method to get all messages by message owner's name?
Ideally, I would like the API to look like GET /api/messages/{name}, but don't think its possible since it conflicts with GET /api/messages/{id}.
I'm thinking of creating the API like this, but I'm not sure how.
GET /api/messages/name/{name} <- (or something along that line)
Solution:
To have GET /api/messages/{name} working without conflicting with GET /api/messages/{id}, change attribute [HttpGet("{id}", Name="GetMessage")] to [HttpGet("{id:long}", Name="GetMessage")] for public IActionResult GetById(long id) method.
To also have GET /api/messages/name/{name} working, add [Route("name/{name}")] attribute to public IEnumerable<Message> GetMessagesByName(string name) method.
you can put parameter type in route, so your code method should be look like that:
// GET api/messages/{id}
[HttpGet("{id:long}", Name = "GetMessage")]
public IActionResult GetById(long id)
{
var message = _repository.GetMessage(id);
if (message == null)
{
return NotFound();
}
return new ObjectResult(message);
}
I think, web api is ignoring parameters types in routes if they are not typed explicitly, so in your example it has two routes like this: api/messages/{object} and when you put explicit type, they are like this: api/messages/{object} and api/messages/{long}

Categories

Resources