I'm still learning ASP.NET, this middleware below is a authorized middleware, this will retrieve all the products in my database.
[Authorize]
// GET: api/ProductsApi
[HttpGet()]
public async Task<ActionResult<IEnumerable<Product>>> GetProduct()
{
return await _context.Product.Where(p => p.Favorite == false).ToListAsync();
}
This next middleware below is where i try calling the api middleware using it URL ("https://localhost:7009/api/ProductsApi")
and convert the returned data/products into a json and display the products in a razor view page.
public async Task<IActionResult> Index()
{
var client = _httpClientFactory.CreateClient();
var response = await client.GetAsync("https://localhost:7009/api/ProductsApi");
if (!response.IsSuccessStatusCode)
{
return RedirectToAction("Index", "Products");
}
var jsonData = await response.Content.ReadAsStringAsync();
List<Product> products = JsonConvert.DeserializeObject<List<Product>>(jsonData);
return View(products);
}
But i'm getting this error.
I did some debugging, the problem was, the api returns a login page but does not display it in my web browser, it instead gives me this error page.
Is there anyway to pass the current loggedin user's credentials inside the header or atleast display the login page so i can login instead of this error message?
Related
This is my code for frontend which calls for my session
await fetch("https://localhost:5001/api/session", {
method: "GET",
credentials: "same-origin",
})
This is my code for the api when sets a session
[HttpGet]
public ActionResult<string> CallMe()
{
HttpContext.Session.SetString("Who", "Doctor Who");
return Ok();
}
This should set a cookie in my browser on call but nothing shows up there
.Code when I try to get the cookie on my other controller
[HttpGet]
public ActionResult<string> CallMeToo()
{
string a = HttpContext.Session.GetString("Who");
return Ok();
}
This returns null after the set call I call this too.
Solved by adding AllowCredentials on services.AddCors to allow cross-origin cookies
I really can't see what I am doing wrong here. I am trying to call a asp.net core web api method that accepts an integer via HttpClient but it returns a 404 error.
HospitalController (Web API)
[HttpGet("{id}")]
[Route("GetById")]
public JsonResult Get(int id)
{
return Json(_hospitalService.Get(id));
}
HospitalController (MVC)
protected string url = "http://localhost:5001/api/Hospital";
[HttpGet]
public async Task<IActionResult> Edit(int id)
{
if (id.Equals(0))
return StatusCode(404);
var accessToken = await HttpContext.GetTokenAsync("access_token");
client.SetBearerToken(accessToken);
HttpResponseMessage responseMessage = await client.GetAsync(url + "/GetById/" + id); //returns 404 error here.
if (responseMessage.IsSuccessStatusCode)
{
var responseData = responseMessage.Content.ReadAsStringAsync().Result;
var hospital = JsonConvert.DeserializeObject<Hospital>(responseData);
var hospitalVM = Mapper.Map<HospitalViewModel>(hospital);
return View(hospitalVM);
}
return View("Error");
}
I have a POST method in the same controller in MVC that works. But this GET method returns a 404 and I can't seem to know why.
There are two route templates being used in the api according to
[HttpGet("{id}")] //Matches GET api/Hospital/5
[Route("GetById")] //Matches GET api/Hospital/GetById
neither of which match what is being called
http://localhost:5001/api/Hospital/GetById/5
Http{Verb} attribute is usually used on RestFul APIs.
When building a REST API, it's rare that you will want to use [Route(...)] on an action method. It's better to use the more specific Http*Verb*Attributes to be precise about what your API supports. Clients of REST APIs are expected to know what paths and HTTP verbs map to specific logical operations.
Reference Routing to Controller Actions
update the route template on the web api to map to the desired route
[Route("api/[controller]")]
public class HospitalController : Controller {
//...code removed for brevity
//Matches GET api/Hospital/GetById/5
[HttpGet("GetById/{id:int}")]
public IActionResult Get(int id) {
return Ok(_hospitalService.Get(id));
}
}
Also HttpClient is meant to be used asynchronously, so the MVC controller would also need to be refactored as mixing blocking calls .Result can cause deadlocks
protected string url = "http://localhost:5001/api/Hospital";
[HttpGet]
public async Task<IActionResult> Edit(int id) {
if (id.Equals(0))
return StatusCode(404);
var accessToken = await HttpContext.GetTokenAsync("access_token");
client.SetBearerToken(accessToken);
HttpResponseMessage responseMessage = await client.GetAsync(url + "/GetById/" + id);
if (responseMessage.IsSuccessStatusCode) {
var responseData = await responseMessage.Content.ReadAsStringAsync();
var hospital = JsonConvert.DeserializeObject<Hospital>(responseData);
var hospitalVM = Mapper.Map<HospitalViewModel>(hospital);
return View(hospitalVM);
}
return View("Error");
}
I'm developing a small PoC WebApi in C#, where a user should be able to view or edit his full name, e.g.:
GET my.api.com/users would return all users (array of JSON objects containing Id and FullName properties, representing users). This is accessible by everyone.
GET my.api.com/users/{id} would return a single user matching that id. This is accessible by everyone.
PUT my.api.com/users/{id} for editing the FullName property of the user matching that id. This should only be accessible on a per-user basis.
I would like to make my PUT request require authentication, and each user only being able to edit his own FullName (e.g. user with id 0 should not be able to make a PUT request to my.api.com/users/1)
Coming from an ASP.NET MVC world, I'm slightly confused as to how to approach this. In MVC, I would simply mark the action as [Authorize] and ensure that the ID of the user making the call matches the actual ID passed in that particular action.
I've understood how authorization would work in WebApi using this Microsoft resource (e.g. using bearer tokens, etc). Would that imply that user registration would be very similar to ASP.NET MVC, i.e. registering using a username/password and then authenticating against the WebApi using bearer tokens?
You can use [Authorize] on WebApi Actions the same way you use it in MVC. Here is a snippet of my old code:
public class MoviesController : ApiController
{
// POST /api/movies/
[Authorize(Roles = RoleName.CanManageMovies)]
[HttpPost]
public IHttpActionResult CreateMovie(MovieDto movieDto)
{
if (!ModelState.IsValid)
return BadRequest();
var movie = Mapper.Map<MovieDto, Movie>(movieDto);
_context.Movies.Add(movie);
_context.SaveChanges();
return Created(new Uri($"{Request.RequestUri}/{movie.Id}"), movie);
}
// PUT /api/movies/1
[Authorize(Roles = RoleName.CanManageMovies)]
[HttpPut]
public IHttpActionResult UpdateMovie(int id, MovieDto movieDto)
{
if (!ModelState.IsValid)
return BadRequest();
var movieInDb = _context.Movies.SingleOrDefault(m => m.Id == id);
if (movieInDb == null)
return NotFound();
Mapper.Map(movieDto, movieInDb);
_context.SaveChanges();
return Ok();
}
// DELETE /api/movies/1
[Authorize(Roles = RoleName.CanManageMovies)]
[HttpDelete]
public IHttpActionResult DeleteMovie(int id)
{
var movieInDb = _context.Movies.SingleOrDefault(m => m.Id == id);
if (movieInDb == null)
return NotFound();
_context.Movies.Remove(movieInDb);
_context.SaveChanges();
return Ok();
}
}
As you can see I'm using the [Authorize] annotation to limit the Actions only to logged in users who are allowed to manage movies.
Using ASP.NET Core 2.0.0 Web API, I'm trying to build a controller to do a database insert. The information can be inserted into the database just fine, but returning a CreatedAtRoute throws an 'InvalidOperationException: No route matches the supplied values.' Everything I've found online so far says this was a bug with early pre-release versions of ASP.NET Core and has since been fixed, but I'm not really sure what to do about this. The following is my controller code:
[Produces("application/json")]
[Route("api/page")]
public class PageController : Controller
{
private IPageDataAccess _pageData; // data access layer
public PageController(IPageDataAccess pageData)
{
_pageData = pageData;
}
[HttpGet("{id}", Name = "GetPage")]
public async Task<IActionResult> Get(int id)
{
var result = await _pageData.GetPage(id); // data access call
if (result == null)
{
return NotFound();
}
return Ok(result);
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] Page page)
{
if (page == null)
{
return BadRequest();
}
await _pageData.CreatePage(page); // data access call
// Return HTTP 201 response and add a Location header to response
// TODO - fix this, currently throws exception 'InvalidOperationException: No route matches the supplied values.'
return CreatedAtRoute("GetPage", new { PageId = page.PageId }, page);
}
Could anyone possibly shed some light on this for me?
The parameters need to match the route values of the intended action.
In this case you need id not PageId
return CreatedAtRoute(
actionName: "GetPage",
routeValues: new { id = page.PageId },
value: page);
I have been through all of the posts about the Facebook deauthorization url and none of them address my issue. I an have ASP.Net Web Api 2 endpoint, but I cannot get the ping to come through correctly. Here is what my Route signature looks like:
[AllowAnonymous, Route("FacebookDeauthorize"), HttpPost]
public async Task<IHttpActionResult> FacebookDeauthorize(string signed_request)
{
//code for reading it
}
This results in a 404, so I tried changing the type to object and getting the type so i could figure out what it was coming through as. It threw a null reference exception when i tried access the object, so I think its just not coming through at all. Since this is Web API, I can't look to the request for a form to get the signed request from. Has anyone successfully gotten this to work with Web Api? Any help/pointers as to what the route signature should be?
For anyone else who finds this, here is the working endpoint for asp.net web api and the Facebook c# sdk, trick is to use a model and not a primitive:
public class FacebookDeauthModel
{
public string signed_request { get; set; }
}
[AllowAnonymous]
[Route("FacebookDeauthorize")]
[HttpPost]
public async Task<IHttpActionResult> FacebookDeauthorize(FacebookDeauthModel model)
{
FacebookClient fb = new FacebookClient();
dynamic signedRequest = JsonConvert.DeserializeObject(fb.ParseSignedRequest("YOUR_APP_SECRET", model.signed_request).ToString());
string FBUserID = signedRequest.user_id;
ApplicationUser user = UserManager.FindBy(x => x.FBAppID == FBUserID);
if (user != null)
{
user.IsActive = false;
user.InactiveReason = "Facebook deauthorized on " + DateTime.UtcNow;
await UserManager.UpdateAsync(user);
}
else
{
_tracer.Error(Request, "FacebookDeauthorize", "Facebook tried to deauthorize a user we do not have record of, FBAppID: {0}", FBUserID);
}
return Ok();
}