Asp.net web api post and get methods are not visible - c#

I am trying to make an api using asp.net core web api. Although I put [HttpGet] and [HttpPost] tags above the methods, only the function that takes parameters appears in the interface.
The methods I created do not appear in the swagger interface
namespace APIproje.Controllers {
public class UsersController : ControllerBase {
private readonly IKullnicilar _userRepositories;
public UsersController(IKullnicilar userRepositories) {
_userRepositories = userRepositories;
}
[HttpGet]
public async Task<IEnumerable<Users>> GetUsers() {
return await _userRepositories.Get();
}
[HttpGet("{id}")]
public async Task<ActionResult<Users>> GetUsers(int id) {
return await _userRepositories.Get(id);
}
[HttpPost]
public async Task<ActionResult<Users>> PostUsers([FromBody] Users user)
{
var newUser = await _userRepositories.Create(user);
return CreatedAtAction(nameof(GetUsers), new { id = newUser.id }, newUser);
}
}
}
Running:
Where am I wrong?

you can use
[ApiController]
[Route("you'r route")]
like this :
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace test
{
[ApiController]
[Route("api/")]
public class UsersController : ControllerBase
{
[HttpGet]
public async Task<IEnumerable<object>> GetUsers()
{
return new List<object>();
}
[HttpGet("{id}")]
public async Task<ActionResult<object>> GetUsers(int id)
{
return Ok(new object());
}
[HttpPost]
public async Task<ActionResult<object>> PostUsers([FromBody] object user)
{
return Ok(new object());
}
}
}

Related

What is the best way to move your code out of the controller and into a helper method class in .NET Core

How can I move repeated code sections out of a controller into a Helper Method class instead of having to repeat the code in .NET Core? Please let me know if I need to provide more details.
I need to move any REPEATED CODE SECTIONS out of this controller so that I can call this method in every other controllers that require it
User controller:
using myApp.Data;
using myApp.Models;
using myApp.Models.ViewModels;
using myApp.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace myApp.Controllers
{
[Authorize]
public class UserController : Controller
{
private readonly ApplicationDbContext db;
private readonly UserManager<ApplicationUser> userManager;
public UserController( ApplicationDbContext db,
UserManager<ApplicationUser> userManager)
{
this.db = db;
this.userManager = userManager;
}
[HttpGet]
public async Task<IActionResult> UpdateUserDetails(UpdateUserViewModel model)
{
if (ModelState.IsValid)
{
var user = await userManager.FindByIdAsync(model.Id);
if (user == null)
{
//Calling Repeated Code in this controller
return UserNotFound();
}
else
{
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.UserName = model.UserName;
user.PhoneNumber = model.PhoneNumber;
}
var result = await userManager.UpdateAsync(user);
if (result.Succeeded)
{
//Calling Repeated Code in this controller
return UpdateSuccess();
}
AddErrors(result);
}
return View(model);
}
//REPEATED CODE SECTION BEGINS (Extracted out of UpdateUserDetails Controller)
public IActionResult UserNotFound()
{
TempData[HelperStatic.ErrorMessage] = HelperStatic.userNotFoundMsg;
return View(HelperStatic.notFoundView);
}
public IActionResult UpdateSuccess()
{
TempData[HelperStatic.SuccessMessage] = HelperStatic.recordUpdatedMsg;
return RedirectToAction(nameof(Index));
}
//REPEATED CODE SECTION ENDS
}
}
A static helper class is already present in the project which has only static constants.
Static helper class used in the above controller:
namespace myApp.Utilities
{
public static class HelperStatic
{
// Messages
public const string SuccessMessage = "Success";
public const string ErrorMessage = "Error";
public const string userNotFoundMsg = "User not found";
public const string recordUpdatedMsg = "Record updated";
// Views
public const string notFoundView = "NotFound";
}
}
I need a different HelperMethod class with reusable action methods. How do I achieve this?
Instead of a helper class, create a controller base class and move all the utility methods into that.
public class BaseController : Controller
{
public IActionResult UserNotFound()
{
TempData[HelperStatic.ErrorMessage] = HelperStatic.userNotFoundMsg;
return View(HelperStatic.notFoundView);
}
public IActionResult UpdateSuccess()
{
TempData[HelperStatic.SuccessMessage] = HelperStatic.recordUpdatedMsg;
return RedirectToAction(nameof(Index));
}
}
And you can use it like this.
public class HomeController : BaseController
{
public IActionResult Index()
{
UserNotFound();
return View();
}
}

ASP.NET Core - Create base controller endpoint - When SubController Endpoints also exist

I have this base controller:
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Vepo.Domain;
using Vepo.Services;
namespace Vepo.Web.Controllers
{
[Route("[controller]")]
[ApiController]
public class VeganItemEstablishmentsController<
TVeganItem,
TVeganItemDto,
TEstablishment,
TEstablishmentDto,
TVeganItemEstablishment,
TVeganItemEstablishmentDto> : ControllerBase
where TVeganItem : VeganItem
where TVeganItemDto : VeganItemDto
where TEstablishment : Establishment
where TEstablishmentDto : EstablishmentDto
where TVeganItemEstablishment : VeganItemEstablishment<TVeganItem, TEstablishment>
where TVeganItemEstablishmentDto : VeganItemEstablishmentDto<TVeganItemDto, TEstablishmentDto>
{
protected readonly IVeganItemEstablishmentsService<TVeganItem, TVeganItemDto, TEstablishment, TEstablishmentDto, TVeganItemEstablishment, TVeganItemEstablishmentDto> service;
public VeganItemEstablishmentsController(
IVeganItemEstablishmentsService<TVeganItem, TVeganItemDto, TEstablishment, TEstablishmentDto, TVeganItemEstablishment, TVeganItemEstablishmentDto> service)
{
this.service = service;
}
[HttpPost]
public async Task<ServiceResponse<TVeganItemEstablishmentDto>> PostVeganItemEstablishment(TVeganItemEstablishmentDto veganItemEstablishment)
{
return await service.AddOrUpdate(veganItemEstablishment);
}
[HttpGet("{id}")]
public ServiceResponse<TVeganItemEstablishmentDto> Fetch(int id)
{
return service.Fetch(id);
}
[HttpGet("search")]
public ActionResult<IEnumerable<TVeganItemEstablishmentDto>> Search(
[FromQuery(Name = "searchterm")] string searchTerm,
[FromQuery(Name = "currentpage")] int pageNumber = 1,
[FromQuery(Name = "pagesize")] int pageSize = 6
)
{
var results = service.Search(searchTerm, pageNumber, pageSize);
return Ok(results);
}
}
}
Then a bunch of subclass controllers like this one, that deal with the subtype of the base type:
using Microsoft.AspNetCore.Mvc;
using Vepo.Domain;
using Vepo.Services;
namespace Vepo.Web.Controllers
{
[Route("[controller]")]
[ApiController]
public class GroceryItemEstablishmentsController :
VeganItemEstablishmentsController<
GroceryItem,
GroceryItemDto,
GroceryItemStore,
GroceryItemStoreDto,
GroceryItemEstablishment,
GroceryItemEstablishmentDto>
{
public GroceryItemEstablishmentsController(
IGroceryItemEstablishmentsService veganItemEstablishmentsService) :
base(veganItemEstablishmentsService)
{
}
}
}
The subclass gives me this endpoint, and it gets a bunch of Groceryitemestablishment objects :
'/groceryitemestablishments/search'
Now I want an endpoint that handles the base class (e.g. it will search for all of the base class objects, disregarding its subType:
'/veganitemestablishments/search'
How can I get that endpoint path /veganitemestablishments/search? I was hoping it would already implicitly be there, because of the base class. But I'm getting 404 error response when attempting to hit the endpoint /veganitemestablishments/search.

405 status when hitting actions for GET in controller

The idea is that with these ItemController methods I should be able to:
Get the items by barcode when hitting "item/query?barcode={barcode}"
Get the items by discount when hitting "item/query?discount={discount}"
Get the items by name when hitting "item/query?name={name}"
Get the items by category when hitting "item/query?category={category}"
And this is my code for actions under the GET verb:
[HttpGet("query/{barcode:int}")]
public async Task<IActionResult> GetByBarcode(int barcode)
{
var item = ...
return Ok(item);
}
[HttpGet("query/{discount:int}")]
public async Task<IActionResult> GetByDiscount(int discount)
{
var items = ...
return Ok(items);
}
[HttpGet("query/{name}")]
public async Task<IActionResult> GetByName(string name)
{
var items = ...
return Ok(items);
}
[HttpGet("query/{category}")]
public async Task<IActionResult> GetByCategory(string category)
{
var items = ...
return Ok(items);
}
The problem is that I keep getting 405 Method Not Allowed when I try to access to any of those actions. I don't know if it's a matter of ambiguity between the methods, can you help me out?
Edit:
Header of ItemController.cs:
using InventoryWebApi.DataAccess;
using InventoryWebApi.DTO;
using InventoryWebApi.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace InventoryWebApi.Controllers
{
[ApiController]
[Route("[controller]")]
public class ItemController : ControllerBase
{
private readonly IRepository repository;
public ItemController(IRepository repository)
...
you are using routing a wrong way.
Your code will not be even run,
your actions shoud be
[HttpGet("query/getbybarcode/{barcode:int})]
public async Task<IActionResult> GetByBarcode(int barcode)
[HttpGet("query/getbydiscount/{discount:int}")]
public async Task<IActionResult> GetByDiscount(int discount)
For this routes you have to use this urls
.../item/query/getbybarcode/{barcode}
..../item/query/getbydiscount/{discount}
if you still want to create url your way you will have to change attribute routing
[HttpGet("query/getbybarcode)]
public async Task<IActionResult> GetByBarcode(int barcode)
[HttpGet("query/getbydiscount")]
public async Task<IActionResult> GetByDiscount(int discount)
and your url should be
.../item/query/getbydiscount?discount={discount}"
..../item/query/getbybarcode?barcode={barcode}"
Update
If you still need to use all your urls for your student project , then the only way is to use one action for all your urls
[HttpGet("query")]
public async Task<IActionResult> Get(int? barcode, int? discount,
string name, string category )
{
if (barcode != null) ...
else if (discount!=null) ...
var item = ...
return Ok(item);
}

HTTP 405 upon GET request when retrieving data

I've been trying to make a REST API for a project. Never done this before technically, but one thing has thrown me for a bit of a baffle. I have a GET request to the server that properly returns a list of clients from my DbContext, but the second get action request I have in right now seems to only return an HTTP 405 error and I can't seem to find out as to why. Any ideas?
I've tried making it an actionResult, perhaps not properly? I know it functions when the action is just a string that returns "value" or another string, but doesn't seem to be able to return an object like the first GET method does
Working get method that gets all the clients in my repository
// GET: api/Client
[HttpGet]
public IEnumerable<Logic.Objects.Client> Get()
{
return _repository.GetClients();
}
Second method that is returning a 405
//GET: api/Client/5
[HttpGet("{id}", Name = "Get")]
public Task<Client> Get(Guid id)
{
return _repository.GetClientByIDAsync(id);
}
Expected results would be if I added a client ID to api/Client/{id}, then it would return that specific client's information, rather than all of them, since that is a unique identifier and wont be the same as any other client, keeping with the RESTful mindset.
405 is the only error it throws unless its an incorrect ID that isnt a Guid, in which case it throw an exception within the method, which also lets me know that it IS getting there, something is just wrong with how it should be returning it's values?
EDIT: This is the whole controller class for those interested. Some methods just arent implemented yet, so they aren't really pertinent to the problem. At least I wouldnt think.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Dbnd.Logic.Interfaces;
using Dbnd.Logic.Objects;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Dbnd.Logic.Objects;
namespace Dbnd.Api.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ClientController : ControllerBase
{
private IRepository _repository;
public ClientController(IRepository repository)
{
_repository = repository;
}
// GET: api/Client
[HttpGet]
public IEnumerable<Logic.Objects.Client> Get()
{
return _repository.GetClients();
}
//GET: api/Client/5
[HttpGet("{id}", Name = "Get")]
public Task<Client> Get(Guid id)
{
return _repository.GetClientByIDAsync(id);
}
// POST: api/Client
[HttpPost]
public ActionResult Post([FromBody, Bind("UserName, Email, PasswordHash")] Client client)
{
_repository.CreateClientAsync(client.UserName, client.Email, client.PasswordHash);
return CreatedAtRoute("Get", client);
}
// PUT: api/Client/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE: api/ApiWithActions/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}
I have another api that works exactly the way you first posted (with actionResults). I couldn't find the problem in your code. It could be settings as you said, but it could also be the way you assemble your URL.
My best bet would be that your repository isn't returning the elemement but again, it should not give you a 405 but instead a 404 not found. I guess the response from your _repository is giving an error.
Maybe a troubleshooting idea is try a Postman request to get and a breakpoint in your controller to see if you could see where the problem is.
I just created a test project to try and reproduce your problem, but it works fine. I followed this tutorial from Microsoft: https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-web-api?view=aspnetcore-3.0&tabs=visual-studio
And here is my resulting controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using WebApplication1.Models;
namespace WebApplication1.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ClientsController : ControllerBase
{
private readonly ClientContext _context;
public ClientsController(ClientContext context)
{
_context = context;
}
// GET: api/Clients
[HttpGet]
public async Task<ActionResult<IEnumerable<Client>>> GetClients()
{
return await _context.Clients.ToListAsync();
}
// GET: api/Clients/5
[HttpGet("{id}")]
public async Task<ActionResult<Client>> GetClient(long id)
{
var client = await _context.Clients.FindAsync(id);
if (client == null)
{
return NotFound();
}
return client;
}
// PUT: api/Clients/5
[HttpPut("{id}")]
public async Task<IActionResult> PutClient(long id, Client client)
{
if (id != client.Id)
{
return BadRequest();
}
_context.Entry(client).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ClientExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Clients
[HttpPost]
public async Task<ActionResult<Client>> PostClient(Client client)
{
_context.Clients.Add(client);
await _context.SaveChangesAsync();
return CreatedAtAction("GetClient", new { id = client.Id }, client);
}
// DELETE: api/Clients/5
[HttpDelete("{id}")]
public async Task<ActionResult<Client>> DeleteClient(long id)
{
var client = await _context.Clients.FindAsync(id);
if (client == null)
{
return NotFound();
}
_context.Clients.Remove(client);
await _context.SaveChangesAsync();
return client;
}
private bool ClientExists(long id)
{
return _context.Clients.Any(e => e.Id == id);
}
}
}
I know it does not answer directly your question, but I hope it gives you food for thought and helps you with troubleshooting.

Changing EF connection string dynamically using a value provided by a jwt (.Net Core WebApi)

This is my first question ever so please bear with me!
I am trying to create a multi-tenant application, where a backend accesses a different database depending on a client ID obtained from a jwt which also serves to authorize API calls.
Ideally I would like to be able to access the web token in my Startup.cs class and generate the connection string with it. Something like this:
public void ConfigureServices(IServiceCollection services)
{
// Stuff
// get jwt claims, something like:
// clientId = Request.Claims["ClientId"]; However Request only seems to be accessible within a controller
// newConnectionString = logic to replace the value found in Configuration.GetConnectionString("Default") with my desired database connection string
services.AddDbContext<KahunaDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Default"))); // current state
// options.UseSqlServer(newConnectionString)); // desired state
}
However, it doesn't seem to be possible to access the jwt outside of a controller.
I can't think of a way to make this happen without horribly messing up the repository pattern I have setup.
Here are some of my other classes which you might need to better understand where I am standing.
Base repository:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace API.Persistence.Repositories
{
public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class
{
private readonly KahunaDbContext context;
public int ClienteId { get; set; }
public BaseRepository(KahunaDbContext context)
{
}
public void Crear(TEntity entity)
{
context.Set<TEntity>().Add(entity);
}
public async Task<List<TEntity>> GetTodos()
{
return await this.context.Set<TEntity>().ToListAsync();
}
public TEntity GetSingle(int id)
{
return this.context.Set<TEntity>().Find(id);
}
public void Borrar(TEntity entity)
{
this.context.Remove(entity);
}
}
public interface IBaseRepository<T>
{
int ClienteId { get; set; }
void Crear(T entity);
Task<List<T>> GetTodos();
void Borrar(T entity);
T GetSingle(int id);
}
}
Base controller:
using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using APIPersistence;
using API.Persistence.Repositories;
namespace API.Controllers
{
public class BaseController<TEntity, TEntityResource> : Controller
{
private readonly IMapper mapper;
private readonly IUnitOfWork unitOfWork;
private readonly IBaseRepository<TEntity> repository;
public BaseController(IBaseRepository<TEntity> repository, IUnitOfWork unitOfWork, IMapper mapper)
{
}
[HttpGet]
[Authorize]
virtual public async Task<IActionResult> Get()
{
List<TEntity> TEntitys = await this.repository.GetTodos();
// List<TEntityResource> TEntityResource = this.mapper.Map<List<TEntity>, List<TEntityResource>>(TEntitys);
return Ok(TEntitys);
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] TEntityResource TEntityResource)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
TEntity tEntity = this.mapper.Map<TEntityResource, TEntity>(TEntityResource);
this.repository.Crear(tEntity);
await this.unitOfWork.CompleteAsync();
return Ok(tEntity);
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
TEntity TEntity = this.repository.GetSingle(id);
this.repository.Borrar(TEntity);
await this.unitOfWork.CompleteAsync();
return Ok(true);
}
[HttpPut("{id}")]
public async Task<IActionResult> Put(int id, [FromBody] TEntityResource TEntityResource)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
TEntity TEntity = this.repository.GetSingle(id);
this.mapper.Map<TEntityResource, TEntity>(TEntityResource, TEntity);
await this.unitOfWork.CompleteAsync();
return Ok(true);
}
}
}
Even if you don't have time to answer the question in detail, any guidance will be extremely helpful. Please let me know if you need any additional information.
Cheers!

Categories

Resources