Cannot find HTTP resource on OData REST API - c#

Hi I have an OData API that I'm trying to run locally but whenever it is being run I cannot load any of the resources. When I run the API, I get this screen with the information on the entities.
enter image description here
Whenever I try to navigate to one using a URL such as http://localhost:51578/Managers, I get an error saying No HTTP resource was found that matches the request URI 'http://localhost:51578/Managers'. Here is the code I have for the Manager Controller.
public class ManagerController : ODataController
{
DatabaseContext db = new DatabaseContext();
private bool ManagerExists(Guid key)
{
return db.Managers.Any(p => p.Id == key);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
[EnableQuery]
public SingleResult<Manager> Get([FromODataUri] Guid key)
{
IQueryable<Manager> result = db.Managers.Where(p => p.Id == key);
return SingleResult.Create(result);
}
[HttpPost]
public async Task<IHttpActionResult> Post(Manager manager)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Managers.Add(manager);
await db.SaveChangesAsync();
return Created(manager);
}
[HttpPatch]
public async Task<IHttpActionResult> Patch([FromODataUri] Guid key, Delta<Manager> manager)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var entity = await db.Managers.FindAsync(key);
if (entity == null)
{
return NotFound();
}
manager.Patch(entity);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ManagerExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(entity);
}
[HttpPut]
public async Task<IHttpActionResult> Put([FromODataUri] Guid key, Manager update)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (key != update.Id)
{
return BadRequest();
}
db.Entry(update).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ManagerExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(update);
}
[HttpDelete]
public async Task<IHttpActionResult> Delete([FromODataUri] Guid key)
{
var manager = await db.Managers.FindAsync(key);
if (manager == null)
{
return NotFound();
}
db.Managers.Remove(manager);
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}
}

Make sure that your WebApiConfig.cs file has the following:
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel(),
defaultHandler: HttpClientFactory.CreatePipeline(innerHandler: new HttpControllerDispatcher(config),
handlers: new[] { new ODataNullValueMessageHandler() }));
This will allow your route to be http://localhost:51578/Managers since we set "routePrefix" to null. If route prefix had something else like "v4" your url would look like http://localhost:51578/v4/Managers.

Related

How to pass Data from action to action using mvc and not in router

i have an action inside of users and i want that action to return the user to another action in another controller but not in the router parameter, Here is a sample
public IActionResult LoginCheck(UserForm user)
{
AuthUser auth = new AuthUser(_context);
var result = auth.IsLoggedIn(user.Email, user.Password);
if(result.isfound==false)
{
return NotFound();
}
result.User.IsAuth = true;
return RedirectToAction("Home","Index",result.User);
}
public async Task<IActionResult> Index(User user)
{
if(user.IsAuth == false)
{
return Unauthorized();
}
just part of the code
Home index did not use the incoming user as it was sent as router parameters i think
Welcome to stackoverflow!
You can use TempData to achieve that.
public IActionResult LoginCheck(UserForm user)
{
AuthUser auth = new AuthUser(_context);
var result = auth.IsLoggedIn(user.Email, user.Password);
if(result.isfound==false)
{
return NotFound();
}
result.User.IsAuth = true;
TempData["user"] = result.User;
return RedirectToAction("Home","Index");
}
Then you can get it in the other action
public async Task<IActionResult> Index()
{
if(TempData["user"] == null)
{
return Unauthorized();
}else{
var someUser= (User)TempData["user"];
}
}
But I do not recommend using TempData for sensitive data.
You can use second action as method:
public async Task<IActionResult> LoginCheck(UserForm user)
{
AuthUser auth = new AuthUser(_context);
var result = auth.IsLoggedIn(user.Email, user.Password);
if(result.isfound==false)
{
return NotFound();
}
result.User.IsAuth = true;
return await Index(result.User);
}
second action
[NonAction] // Indicates that a controller method is not an action method.
public async Task<IActionResult> Index(User user)
{
if(user == null)
{
return Unauthorized();
}
else{
var someUser= user;
}
}
Use redirection make browser to handle this transfer and it is slower.

Variable in Authorization Attribute .net in base Controller

In my application, I'm trying to build an authorization - privilege based authentication.
For better understanding here's how my privileges are named and stored in my database : "GET_USERS" , "GET_USER", "DELETE_USER" ...
What I want to do is to specify the privilege name in the authorization attribute from my base controller, but the problem is that the authorization attribute only allows constant parameters.
Here's my base Controller :
public class BaseController<T> : ControllerBase where T : class
{
protected readonly ConcreetDataContext _context;
protected readonly IBaseRepository<T> _repo;
protected readonly INotificationRepository _notificationRepo;
protected readonly IHubContext<NotificationHub> _hubContext;
protected readonly DbSet<T> _dbSet;
public BaseController(IHubContext<NotificationHub> hubContext,
ConcreetDataContext context,
IBaseRepository<T> repo,
INotificationRepository notifRepo)
{
_context = context;
_hubContext = hubContext;
_repo = repo;
_dbSet = _context.Set<T>();
_notificationRepo = notifRepo;
}
// GET: api/[items]
[HttpGet]
// HERE's THE ISSUE
[PrivilegeAuthorize("GET_"+typeof(T).Name.toUpper()] // this is not allowed
public async Task<ActionResult<IEnumerable<T>>> Get([FromQuery] GlobalParams globalParams)
{
Type t = typeof(T);
Console.WriteLine(t.Name.ToUpper());
var classes = await PagedList<T>.CreateAsync(_repo.Get(globalParams),globalParams.PageNumber,globalParams.PageSize);
Response.AddPagination(classes.CurrentPage, classes.PageSize, classes.TotalCount, classes.TotalPages);
return Ok(classes);
}
// GET: api/[items]/5
[HttpGet("{id}")]
public virtual async Task<ActionResult<T>> Get(int id)
{
var item = await this._repo.GetByID(id);
if (item == null)
{
return NotFound();
}
return item;
}
// PUT: api/[items]/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
public async Task<IActionResult> Put(int id, T item)
{
// if (id != item.ID)
// {
// return BadRequest();
// }
try
{
await this._repo.Update(item);
// Creating the notification
await this._notificationRepo.CreateNotification("Update",typeof(T).ToString(),"Updated "+typeof(T).ToString()+" with ID : "+id);
}
catch (DbUpdateConcurrencyException)
{
if (!(await Exists(id)))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Classes
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async virtual Task<ActionResult<T>> Post(T item)
{
await this._repo.Insert(item);
await this._notificationRepo.CreateNotification("Create",typeof(T).ToString(),"Created "+typeof(T).ToString());
return CreatedAtAction("Get", item);
}
// DELETE: api/Classes/5
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var item = await _dbSet.FindAsync(id);
if (item == null)
{
return NotFound();
}
_dbSet.Remove(item);
await _context.SaveChangesAsync();
var notification = await this._notificationRepo.CreateNotification("Delete",typeof(T).ToString(),"Deleted "+typeof(T).ToString());
// Invoking BroadCastToUserFunction
var useID = Request.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value;
await _hubContext.Clients.User(useID).SendAsync("simo",notification);
return NoContent();
}
private async Task<bool> Exists(int id)
{
var item = await this._repo.GetByID(id);
if(item != null)
return true;
return false;
}
[HttpGet("count")]
public async Task<ActionResult<int>> Count([FromQuery] GlobalParams globalParams)
{
return await this._repo.Count(globalParams);
}
}
Thank you in advance !
As Lasse V. Karlsen pointed out in comments: "base class is compiled once, it is not compiled for each Variant, so the compiler will have to figure out what to pass as a string there once".
So I went with the following solution for the moment :
[PrivilegeAuthorize("GET_USERS")]
public override Task<ActionResult<IEnumerable<City>>> Get([FromQuery] GlobalParams globalParams)
{
return base.Get(globalParams);
}
I should override each method where I want to apply this authorization attribute.

Testing crud operations using xunit in an asp.net web api core application [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I am completely new to unit testing and will like to write unit test for my controller
below using xunit and moq. I understand that you can only mock database transactions and
make the unit test independent of the database.
How can I go about writing xunit tests for these? any documentation or sample code will be appreciated.
I know how to add the xnit test project to my solution.
using System;
using System.Threading.Tasks;
using CoreServices.Models;
using CoreServices.Repository;
using Microsoft.AspNetCore.Mvc;
namespace CoreServices.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class PostController : ControllerBase
{
IPostRepository postRepository;
public PostController(IPostRepository _postRepository)
{
postRepository = _postRepository;
}
[HttpGet]
[Route("GetCategories")]
public async Task<IActionResult> GetCategories()
{
try
{
var categories = await postRepository.GetCategories();
if (categories == null)
{
return NotFound();
}
return Ok(categories);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpGet]
[Route("GetPosts")]
public async Task<IActionResult> GetPosts()
{
try
{
var posts = await postRepository.GetPosts();
if (posts == null)
{
return NotFound();
}
return Ok(posts);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpGet]
[Route("GetPost")]
public async Task<IActionResult> GetPost(int? postId)
{
if (postId == null)
{
return BadRequest();
}
try
{
var post = await postRepository.GetPost(postId);
if (post == null)
{
return NotFound();
}
return Ok(post);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpPost]
[Route("AddPost")]
public async Task<IActionResult> AddPost([FromBody]Post model)
{
if (ModelState.IsValid)
{
try
{
var postId = await postRepository.AddPost(model);
if (postId > 0)
{
return Ok(postId);
}
else
{
return NotFound();
}
}
catch (Exception)
{
return BadRequest();
}
}
return BadRequest();
}
[HttpPost]
[Route("DeletePost")]
public async Task<IActionResult> DeletePost(int? postId)
{
int result = 0;
if (postId == null)
{
return BadRequest();
}
try
{
result = await postRepository.DeletePost(postId);
if (result == 0)
{
return NotFound();
}
return Ok();
}
catch (Exception)
{
return BadRequest();
}
}
[HttpPost]
[Route("UpdatePost")]
public async Task<IActionResult> UpdatePost([FromBody]Post model)
{
if (ModelState.IsValid)
{
try
{
await postRepository.UpdatePost(model);
return Ok();
}
catch (Exception ex)
{
if (ex.GetType().FullName == "Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException")
{
return NotFound();
}
return BadRequest();
}
}
return BadRequest();
}
}
}
You can just instantiate that controller with new PostController(postRepository).
If you need a mock for the post repository you can use a library called Moq. (Use nuget to get it). Simply write:
using Moq;
Mock<IPostRepository> postRepositoryMock = new Mock<IPostRepository>();
PostController controller = new PostController(postRepositoryMock.Object);
then you can use the Setup method on the Mock to return what you want. For example to Mock the Get method in a way that returns a test Post for any given Id of type int?:
postRepositoryMock.Setup(it => it.Get(It.IsAny<int?>()).Returns(new Post() { //here what you need to build your post object })
Then you can execute the code to get the result from the controller, for example (Test Code is made using NUnit, I don't use xunit but the concept is the same):
using Moq;
[TestFixture]
public class ExampleFixture()
{
[Test]
public void ExampleTest()
{
Mock<IPostRepository> postRepositoryMock = new Mock<IPostRepository>();
PostController controller = new PostController(postRepositoryMock.Object);
postRepositoryMock.Setup(it => it.Get(It.IsAny<int?>()).Returns(new Post() { //here what you need to build your post object })
var result = controller.GetPost(1);
Assert.That(result, Is.Not.Null, "Unexpected null result");
var retrievedPostContent = result as OkNegotiatedContentResult<Post>;
Assert.That(retrievedPostContent, Is.Not.Null, "Unexpected null retrievedPost");
var retrievedPost = result.Content;
Assert.That(retrievedPost.Id, Is.EqualTo(1), "retrievedPost.Id is unexpected")
}
}
with xUnit should became:
using Xunit;
using Moq;
namespace ATestNamespace
{
public class TestFixture
{
public TestFixture()
{
}
[Fact]
public void Test()
{
Mock<IPostRepository> postRepositoryMock = new Mock<IPostRepository>();
PostController controller = new PostController(postRepositoryMock.Object);
postRepositoryMock.Setup(it => it.Get(It.IsAny<int?>()).Returns(new Post() { //here what you need to build your post object });
var result = controller.GetPost(1);
Assert.True(result != null,"Unexpected null result");
var retrievedPostResult = result as OkNegotiatedContentResult<Post>;
Assert.True(retrievedPostResult != null, "Unexpected null retrievedPost");
var retrievedPost = result.Content;
Assert.True(retrievedPost.Id == 1, "retrievedPost.Id is unexpected")
}
}
}

GET action with multiple ids inputted .NET

It`s possible to create a GET action based on multiple id's inputted?
For example how can I change this method to be GetCustomer([FromRoute] int id, int code_id)?
// GET: api/Customer/5
[HttpGet("{id}")]
public async Task<IActionResult> GetCustomer([FromRoute] int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var customerMaster = await _context.CustomerMaster.SingleOrDefaultAsync(m => m.Code == id);
if (customerMaster == null)
{
return NotFound();
}
return Ok(customerMaster);
}
Use AttributeRouting:
// GET: api/Customer/5/3
[Route("api/Customer/{id}/{code_id}")]
public async Task<IActionResult> GetCustomer(int id, int code_id)
{
...
return Ok(customer);
}

Asp.Net Web Api Odata V4 - Concurrency Checking

Below code is generated from Wep Api Odata v4 scaffolding tool.
PUT Method
public IHttpActionResult Put([FromODataUri] string key, Delta<Product> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Product product = db.Products.Find(key);
if (product == null)
{
return NotFound();
}
patch.Put(product);
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(product);
}
PATCH Method:
[AcceptVerbs("PATCH", "MERGE")]
public IHttpActionResult Patch([FromODataUri] string key, Delta<Product> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Product product = db.Products.Find(key);
if (product == null)
{
return NotFound();
}
patch.Patch(product);
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(product);
}
Rowversion field in model
[Timestamp]
public byte[] RowVersion { get; set; }
Questions:
I need to implement concurrency check. How can I check optimistic
concurrency in a Odata way (Using Etag)?
In above code DbUpdateConcurrencyExceptionnever thrown. Any
reason?
There is a attribute called [ConcurrencyCheck] what is the use
of it? Can I use this?
Providing a code sample will be highly appreciated!.
i solve this checking my self the concurrency fields because the patch or put dont does.
This is my code where plato.TimeStamp is the [Timestamp] property.
public async Task<IHttpActionResult> Put([FromODataUri] int key, Delta<Plato> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Plato plato = await db.Platos.FindAsync(key);
if (plato == null)
{
return NotFound();
}
//Here save the current value in the DB
string timeStamp = Convert.ToBase64String(plato.TimeStamp);
patch.Put(plato);
try
{
//Here plato.TimeStamp is update from remote, must be equal to stored value
if (timeStamp != Convert.ToBase64String(plato.TimeStamp))
{
throw new DbUpdateConcurrencyException();
}
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PlatoExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(plato);
}

Categories