How to remove value from route int .net core - asp.net-core

I'm writing .net core mvc app. I have two methods
[HttpGet("{action}/{userId}")]
public async Task<IActionResult> ChangeUser(string userId)
{
var user = await _dbContext.Users.Where(x => x.Id == userId).FirstOrDefaultAsync();
...
return View(new ChangeUserVM());
}
[HttpPost("{action}/{userId}")]
public async Task<IActionResult> ChangeUser(ChangeUserVM user)
{
...
}
I need routing in the HttpGet method to get into the first method. But then i want to get from the html to the second method and i have to use routing again otherwise i get 405. How i can get rid of routing in the second method?

I can’t verify my suggestion right now, but over second method, try to remove from HttpPost attribute “userId”.
[HttpPost(“action”)]
public async Task<IActionResult> ChangeUser(ChangeUserVM user)

Related

HTTP Errors When Using POST Methods

I can't quite seem to figure out how to call HTTP POST functions from my Blazor WASM project hosted with ASP.NET. I am having trouble finding any examples of using POST methods past .NET 6 likely because it's so new. I've tried setting content-headers to JSON and many different ways of retrieving the request body from the actual controller function, but I just get 500, 415, and 400 errors. I've also tried not using model binding the the controller function, but to no avail. I do not believe this is the issue though, as using the [ApiController] attribute infers proper model binding as far as I know. I can only imagine the issue stems from the HTTP call.
The service that calls the method:
public async Task CreateUser(User user)
{
await _httpClient.PostAsJsonAsync("users", user);
}
The controller function:
[HttpPost]
public async Task PostUser(User user)
{
_context.Users.Add(user);
await _context.SaveChangesAsync();
}
The given from the above code is just a simple 400 error.
Also, I've added a test user into the database manually, and I'm able to retrieve it without any issues.
Here's some code from one of my demo projects showing API calls to get WeatherForecast records.
Here's the Web Assembly project DataBroker:
public class WeatherForecastAPIDataBroker : IWeatherForecastDataBroker
{
private readonly HttpClient? httpClient;
public WeatherForecastAPIDataBroker(HttpClient httpClient)
=> this.httpClient = httpClient!;
public async ValueTask<bool> AddForecastAsync(WeatherForecast record)
{
var response = await this.httpClient!.PostAsJsonAsync<WeatherForecast>($"/api/weatherforecast/add", record);
var result = await response.Content.ReadFromJsonAsync<bool>();
return result;
}
public async ValueTask<bool> DeleteForecastAsync(Guid Id)
{
var response = await this.httpClient!.PostAsJsonAsync<Guid>($"/api/weatherforecast/delete", Id);
var result = await response.Content.ReadFromJsonAsync<bool>();
return result;
}
public async ValueTask<List<WeatherForecast>> GetWeatherForecastsAsync()
{
var list = await this.httpClient!.GetFromJsonAsync<List<WeatherForecast>>($"/api/weatherforecast/list");
return list!;
}
}
And here's the controller it's calling:
namespace Blazr.Demo.Controllers;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private IWeatherForecastDataBroker weatherForecastDataBroker;
public WeatherForecastController(IWeatherForecastDataBroker weatherForecastDataBroker)
=> this.weatherForecastDataBroker = weatherForecastDataBroker;
[Route("/api/weatherforecast/list")]
[HttpGet]
public async Task<List<WeatherForecast>> GetForecastAsync()
=> await weatherForecastDataBroker.GetWeatherForecastsAsync();
[Route("/api/weatherforecast/add")]
[HttpPost]
public async Task<bool> AddRecordAsync([FromBody] WeatherForecast record)
=> await weatherForecastDataBroker.AddForecastAsync(record);
[Route("/api/weatherforecast/delete")]
[HttpPost]
public async Task<bool> DeleteRecordAsync([FromBody] Guid Id)
=> await weatherForecastDataBroker.DeleteForecastAsync(Id);
}
The Repo for the Demo Project Blazor.Demo
Controller Code
Data Broker Code

Is it possible to restrict an action method to "only redirect-to access"?

Using ASP.NET Core 3.1 - MVC, I have an HTTP Post action method that gets data from a client and works on database. Because this action method was very long and untidy and many repeated codes, I decided to simplify this action method and use Redirect-to. Something like this :
[HttpPost]
[ValidateAntiForgeryToken]
[Route("MainActionMethod")]
public async Task<IActionResult> MainActionMethod([FromBody]object jsonData)
{
. . .
if (condition a)
return RedirectToAction("Action1");
if (condition b)
return RedirectToAction("Action2");
. . .
}
Action1 must be HTTPGet to be redirected and so a user can type a URL like this and modify my database
http://www.example.com/?param1="Hello"&param2="Stacky"
How could I disable access to HTTP GET Action1 from the browser and be accessed only from other action methods or only by redirect-to?
There is an attribute Referer in the header of Request. If it is accessed from a browser, its value is empty. Use this to determine the subsequent processing procedure.
[HttpPost]
[ValidateAntiForgeryToken]
[Route("MainActionMethod")]
public async Task<IActionResult> MainActionMethod([FromBody]object jsonData)
{
if (true)
return RedirectToAction("Action1");
}
public IActionResult Action1()
{
StringValues header ;
Request.Headers.TryGetValue("Referer",out header);
if (header.Count==0)
{
return BadRequest();
}
return Ok("Action1");
}

Unable to configure route for Get in WebApi 2

I'm struggling with something very basic. I'm trying to be get a response from my WebApi2 restful service, and I can't.
I have not edited the default WebApi (WebApiConfig.cs) route.
This is the controller
public class AboutController
{
[Route("api/about/{id:int}/{service1}/{service2}")]
public async Task<IHttpActionResult> Get(int accountId, string mainservice, string secondaryservice)
{
//logic
}
}
If I navigate (in a browser) to http://localhost:58090/api/about I get the error message The requested resource does not support http method 'GET'. I guess this makes sense, as it doesn't match the route (path).
If I update the path to something which matches the signature, such as http://localhost:58090/api/about/1/a/b I get the error message No action was found on the controller About' that matches the request.
Even if I add [HttpGet] to the controller, it makes no difference.
As a sanity test, I updated to
public class AboutController
{
public async Task<IHttpActionResult> Get()
{
//logic
}
}
and it does what is expected. I'm lost as to why adding the parameters has confused things so much.
I'm lost as to what I've done wrong
The route must match the parameters
[Route("api/about/{id:int}/{service1}/{service2}")]
public async Task<IHttpActionResult> Get(int id, string mainService, string secondaryService)
{
The above won't work, because it is expecting to see service1 and service2 based upon the route.
Update as per the example below
[Route("api/about/{id:int}/{service1}/{service2}")]
public async Task<IHttpActionResult> Get(int id, string service1, string service2)
{

ASP.NET Core routing error

I have a route like http://localhost:63037/api/futuresMarginRuns/7/data which is working however another controller API with route http://localhost:63037/api/futuresMarginRuns/2018-07-11/data is not working, even the breakpoint in the controller API is not hit.
Here are the API signatures
[HttpGet]
[Route("/api/futuresMarginRuns/{id}/data")]
public async Task<IActionResult> GetFuturesMarginRunDataAsync(long id)
{
var data = await _repository.GetAllAsync(id).ConfigureAwait(false);
return Ok(data);
}
[HttpGet]
[Route("/api/futuresMarginRuns/{runDate}/data")]
public async Task<IActionResult> GetFuturesMarginRunDataByDateAsync(DateTime runDate)
{
var data = await _repository.GetAllAsync(runDate).ConfigureAwait(false);
return Ok(data);
}
In the first case I get json data but in the second one the breakpoint is not hit so looks like the route is not mapped to the API properly in which case I would expect an error, but i get empty []
How can I the API to work?
Thanks
You need to add some route constraints to your routes. Route constraints tell the routing engine that if id is supposed to be an int, only match that route if the text in that spot can be converted to an int (and similarly with dates, etc).
So I would change your routes to the following:
[HttpGet]
[Route("/api/futuresMarginRuns/{id:long}/data")]
public async Task<IActionResult> GetFuturesMarginRunDataAsync(long id)
[HttpGet]
[Route("/api/futuresMarginRuns/{runDate:datetime}/data")]
public async Task<IActionResult> GetFuturesMarginRunDataByDateAsync(DateTime runDate)

MVC 6 Web Api: Resolving the location header on a 201 (Created)

In Web Api 2.2, we could return the location header URL by returning from controller as follows:
return Created(new Uri(Url.Link("GetClient", new { id = clientId })), clientReponseModel);
Url.Link(..) would resolve the resource URL accordingly based on the controller name GetClient:
In ASP.NET 5 MVC 6's Web Api, Url doesn't exist within the framework but the CreatedResult constructor does have the location parameter:
return new CreatedResult("http://www.myapi.com/api/clients/" + clientId, journeyModel);
How can I resolve this URL this without having to manually supply it, like we did in Web API 2.2?
I didn't realise it, but the CreatedAtAction() method caters for this:
return CreatedAtAction("GetClient", new { id = clientId }, clientReponseModel);
Ensure that your controller derives from MVC's Controller.
In the new ASP.NET MVC Core there is a property Url, which returns an instance of IUrlHelper. You can use it to generate a local URL by using the following:
[HttpPost]
public async Task<IActionResult> Post([FromBody] Person person)
{
_DbContext.People.Add(person);
await _DbContext.SaveChangesAsync();
return Created(Url.RouteUrl(person.Id), person.Id);
}
There is an UrlHelper class which implements IUrlHelper interface.
It provides the requested functionality.
Source code
There is also CreatedAtRoute:
public async Task<IActionResult> PostImpl([FromBody] Entity entity)
{
...
return CreatedAtRoute(entity.Id, entity);
//or
return CreatedAtRoute(new { id = entity.Id }, entity);
}
My GET action has a route name
[HttpGet("{id:int}", Name = "GetOrganizationGroupByIdRoute")]
public async Task<IActionResult> Get(int id, CancellationToken cancellationToken = default(CancellationToken))
{
...
}
And my POST action uses that route name to return the URL
[HttpPost]
public async Task<HttpStatusCodeResult> Post([FromBody]OrganizationGroupInput input, CancellationToken cancellationToken = default(CancellationToken))
{
...
var url = Url.RouteUrl("GetOrganizationGroupByIdRoute", new { id = item.Id }, Request.Scheme, Request.Host.ToUriComponent());
Context.Response.Headers["Location"] = url;
...
}
Resulting response using Fiddler
Hope that helps.
I use this simple approximation based on the Uri being served at the web server:
[HttpPost]
[Route("")]
public IHttpActionResult AddIntervencion(MyNS.MyType myObject) {
return Created<MyNS.MyType>(Request.RequestUri + "/" + myObject.key, myObject);
}