Complete request execution from inner method in Web API - asp.net-core

I have API that based on ASP.NET Core 2.2 and I want to return result not from my public method that handles request but from an inner method.
public class UsersController : MainController
{
[HttpGet("{id:int}")]
public IActionResult Get(int id)
{
var value = GetSomeValue();
}
private string GetSomeValue()
{
// and here I want to return result to user not even returning result to calling method `Get`
return "";
}
}
I can set status code to response through HttpContext.Response.StatusCode and I know how to set body to the response but I don't know either could I return response to user from GetSomeValue method or not. May be somehow using HttpContext?

I'm not really if this is what you're asking but here are my two cents:
I want to return result not from my public method that handles request but from an inner method.
Well this is not trivial, what you're asking If I get it right is to manipulate the HttpContext so you start the response on an inner method instead of returning from your controller, which is the right way.
I don't know why you would want to do that but, I guess you can get some advice here
In any case I don't get why don't you just:
return Ok(value);
like:
public class UsersController : MainController
{
[HttpGet("{id:int}")]
public IActionResult Get(int id)
{
var value = GetSomeValue();
return Ok(value);
}
private string GetSomeValue()
{
// and here I want to return result to user not even returning result to calling method `Get`
return "";
}
}

Related

Pass data to controller method from AuthorizationFilter in ASP.Net Core Web API

I have implemented a custom AuthorizationFilter in my ASP.NET Core Web API. The filter checks for a code in the request header and identifies the userID based on the code.
I want this userID to be reused in my GET/POST call. How can I transfer data from my AuthorizationFilter to my calling method to avoid duplicate database calls?
AuthorizationFilter function:
public void OnAuthorization(AuthorizationFilterContext context)
{
string header = _httpContextAccessor.HttpContext.Request.Headers["CODE"].ToString();
int userID = GetAccessData(header);
//Want to use this UserID in my calling method
}
Calling function in my controller:
[HttpGet]
public IActionResult UseData(Model modelObj)
{
// I want to use userID retrieved at filter level here.
}
transfer data from my AuthorizationFilter to my calling method
Below is a work demo, I use two options, you can refer to it.
CustomAuthenticationFilter :
public class CustomAuthenticationFilter : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
//you can use your userID
//Option 1
context.HttpContext.Items["userID"] = "1111111";
//Option 2
var userId = "22222";
context.RouteData.Values.Add("UserId", userId);
}
}
Action:
[HttpGet]
[CustomAuthenticationFilter]
public IActionResult UseData()
{
// I want to use userID retrieved at filter level here.
var userID = HttpContext.Items["userID"];
var userid2 = RouteData.Values["UserId"];
return Ok(userID);
}
result:

ASP.NET Core WEB API Problem with GET parameters in API method

I have this controller
[ApiController]
[Route("api/[controller]")]
public class FamiliesController : ControllerBase
{
readonly FamilyFinanceContext db;
public FamiliesController(FamilyFinanceContext context)
{
db = context;
}
[HttpDelete("deletefamily")]
public async Task<ActionResult<Family>> DeleteFamilly(int id)
{
Family user = db.Families.FirstOrDefault(x => x.Id == id);
if (user == null)
{
return NotFound();
}
db.Families.Remove(user);
await db.SaveChangesAsync();
return Ok(user);
}
}
after call https://localhost:44373/api/families/deletefamily?id=2 i have this error - HTTP ERROR 405
In theory this GET parameters must work. What i done not correctly?
As ESG stated, your trying to do a DELETE, so you need to use the DELETE verb, not the GET verb. You're getting a 405 Method Not Allowed for this reason (you cannot use GET on a DELETE action). You should use a tool like PostMan (https://www.postman.com/) to create your DELETE requests, since you can't really do it easily just in a browser.
To fall more in line with REST convention, you should consider changing your DELETE method slightly:
[HttpDelete("{id}")]
public async Task<ActionResult> DeleteFamily([FromRoute] int id)
{
Family user = db.Families.FirstOrDefault(x => x.Id == id);
if (user == null)
{
return NotFound();
}
db.Families.Remove(user);
await db.SaveChangesAsync();
return NoContent();
}
You would then call this as DELETE https://localhost:44373/api/families/2
By using the [HttpDelete("{id}"] attribute, your moving the id from the query string to the URI, which is more in line with REST convention (the URI represents an object). Query string parameters are more typically used for optional capabilities, such as filtering, sorting, etc and not the endpoint representing the object itself.
Typically DELETE actions do not return content, but that is up to you. If you really want to return the user object, then stick with Ok(user), but NoContent is more typical of the DELETE verb.

Route to controller based on query string

Problem: We are upgrading from a legacy system, so solutions are constrained. I am trying to route to an unauthorized controller if a specific query string is present. If it is not present, the user is routed to the authorized controller. This is on ASP.Net Core 2.1.
Is it possible to set the controller to route based on query string? I've tried
[/home/[action]?query={query}] -> Leads to runtime error due to '?'
[/home/[action]/{query}] - > maps to /home/index/1 (not what I need)
Thanks for any help!
Edit: Alternatively, is it possible to have a separate controller Action that depends on the query parameter?
public IActionResult Index(){}
public IActionResult Index([FromQuery]string query){}
Routing doesn't seem to distinguish between these two.
You can use IActionConstraint and IParameterModelConvention interfaces for that. In short, create an IActionConstraint like this:
public class RequiredFromQueryActionConstraint : IActionConstraint
{
private readonly string _parameter;
public RequiredFromQueryActionConstraint(string parameter)
{
_parameter = parameter;
}
public int Order => 999;
public bool Accept(ActionConstraintContext context)
{
if (!context.RouteContext.HttpContext.Request.Query.ContainsKey(_parameter))
{
return false;
}
return true;
}
}
If a matching parameter is not found on the request's query string, then it will return false from the Accept method.
Than create RequiredFromQueryAttribute class like this:
public class RequiredFromQueryAttribute : FromQueryAttribute, IParameterModelConvention
{
public void Apply(ParameterModel parameter)
{
if (parameter.Action.Selectors != null && parameter.Action.Selectors.Any())
{
parameter.Action.Selectors.Last().ActionConstraints.Add(new RequiredFromQueryActionConstraint(parameter.BindingInfo?.BinderModelName ?? parameter.ParameterName));
}
}
}
Than you could decorate your mandatory query string parameters with this attribute:
[Route("api/[controller]")]
public class ValuesController : Controller
{
[HttpGet("{id}")]
public string Get(int id, [RequiredFromQuery]string foo, [RequiredFromQuery]string bar)
{
return id + " " + foo + " " + bar;
}
}
From now than, only the following URL GET api/values/5?foo=a&bar=b would lead into the action above, all other combinations of parameters would result in response with status 404, which you can eventually replace with what you want.
You can find more info at this link https://www.strathweb.com/2016/09/required-query-string-parameters-in-asp-net-core-mvc/

How to retrieve current application root URL in .net core within a static method?

I am currently using the Request.Scheme and Request.Host to composite Uri object to get AbsoluteUri for my .net core MVC application.
Uri location = new Uri($"{Request.Scheme}://{Request.Host}");
string applicationRootURL = location.AbsoluteUri;
But this only works in a non-static method.
As I need to re-use this method in another controller, I am thinking to make this action method static. If I do that, the compiler will complaint about the Request.Scheme and Request.Host.
I am wondering what's other options I have to achieve this?
Thank you.
UPDATE:
This is what I have for ControllerA with ActionMethodA
public class ControllerA
{
public bool ActionMethodA()
{
Uri location = new Uri($"{Request.Scheme}://{Request.Host}");
string applicationRootURL = location.AbsoluteUri;
return false;
}
}
And in another ControllerB, I want to ActionMethodB to invoke ActionMethodA from ControllerA:
public class ControllerB
{
public void ActionMethodB()
{
var result = ActionMethodA();
}
}
Is creating an Extension Method to the ControllerA is the most proper way to handle this kind of scenario?
Thank you.
You can also define an extension method directly for the HttpRequest class and use the BuildAbsolute method of the UriHelper class to build the uri.
public static class HttpRequestExtensions
{
public static string GetURI(this HttpRequest request)
{
return UriHelper.BuildAbsolute(request.Scheme, request.Host);
}
}
And use it:
public IActionResult ContollerMethod()
{
var uri = Request.GetURI();
// your code
}
You can write an extension method to a controller or HttpContext object. In the following example I have added an extension method to the controller.
public static class ControllerExtensions
{
public static string GetURI(this Controller controller)
{
Uri location = new Uri($"{ controller.Request.Scheme}://{controller.Request.Host}");
string applicationRootURL = location.AbsoluteUri;
return applicationRootURL;
}
}
Once the extension method is written you can call it in the following manner.
public IActionResult Index()
{
var url = this.GetURI();
return View();
}
Make sure to import namespace of an extension method in your calling code

ASP.NET Core MVC 1.0 multiple post method in one Controller

I can do this by using this code :
[HttpPost("SampleRoute1")]
public JsonResult Post([FromBody]SampleModel1 value)
{
.....Functionone.....
return Json("");
}
[HttpPost("SampleRoute2")]
public JsonResult Post([FromBody]SampleModel2 value)
{
.....Functiontwo.....
return Json("");
}
but i cant do this :
[HttpPost("SampleRoute1")]
public JsonResult Post([FromBody]SampleModel1 value)
{
.....Functionone.....
return Json("");
}
[HttpPost("SampleRoute2")]
public JsonResult Post([FromBody]SampleModel1 value)
{
.....Functiontwo.....
return Json("");
}
it gives error "Type 'Controller1' already defines a member called 'Post' with the same parameter types"
so is there any way that i can make two Post in one controller with same paramter but with different route?
like this :
Posting(SampleModel1) => "Controller1\SampleRoute1" => Doing Function1
Posting(SampleModel1) => "Controller1\SampleRoute2" => Doing Function2
Yes, you can do that. Problem is that you're trying to have two methods in a class that have same name & parameters and that's not possible. You should change name of your methods to something different.
Note that the action name & Post request type are already specified in the HttpPost attribute so you don't have to rely on the method name.
[HttpPost("SampleRoute1")]
public JsonResult Aaa([FromBody]SampleModel1 value)
{
.....Functionone.....
return Json("");
}
[HttpPost("SampleRoute2")]
public JsonResult Bbb([FromBody]SampleModel1 value)
{
.....Functiontwo.....
return Json("");
}
You are getting the error because you have 2 methods that are identical. How would you know which one to execute? Are you basing this on the routes that you defined?
If I gave you 2 identical red apples to eat, there is no difference between the 2 apples, and I told you to eat the correct apple, would you know which is the correct apple?
You are going to have to change your method names so that they are unique and identifiable.
[HttpPost("SampleRoute1")]
public ActionResult Function1(SampleModel1 model)
{
return Json("");
}
[HttpPost("SampleRoute2")]
public ActionResult Function2(SampleModel1 model)
{
return Json("");
}
So based on the above, the following will happen:
So now when posting SampleModel1, using route Controller1\SampleRoute1 will execute action method Function1
So now when posting SampleModel2, using route Controller1\SampleRoute2 will execute action method Function2.