Using ApiExplorerSettings GroupName in Route in ASPNET Core - asp.net-core

In ASP.NET Core - Web API project
Using [ApiExplorerSettings(GroupName = "<group-name>")] decorated on ApiController
and in [Route] attribute I want to refer above GroupName property value.
Also note I do have [ApiVersion("<some-version>")] on same controller to classify further.
Here are some samples to explain:
Example 1:
Attribute on LeadController: [ApiVersion("1.0"), ApiExplorerSettings(GroupName = "sales"), [Route("api/{groupName}/v{version:apiVersion}/leads"]
Expected translated route format: /api/sales/v1/leads
Attribute on AccountsController: [ApiVersion("2.1"), ApiExplorerSettings(GroupName = "finance"), [Route("api/{groupName}/v{version:apiVersion}/accounts"]
Expected translated route format: /api/finance/v2.1/leads
In above {version:apiVersion} gives me ApiVersion value (I assume because that attribute has ToString set to version value). But when I try {groupName} or {grp:groupName} or {grp:ApiExplorerSettings.GroupName} - none of them works. How can access this group name in route attribute?

Do you have any special settings somewhere else, it works fine on my side.
LeadController:
[ApiVersion("1.0"), ApiExplorerSettings(GroupName = "sales"),Route("api/{groupName}/v{version:apiVersion}/leads")]
[ApiController]
public class LeadController : ControllerBase
{
public string Get()
{
return "sales group";
}
}
AccountsController:
[ApiVersion("2.1"), ApiExplorerSettings(GroupName = "finance"), Route("api/{groupName}/v{version:apiVersion}/accounts")]
[ApiController]
public class AccountsController : ControllerBase
{
public string Get()
{
return "finance group";
}
}
Result:

ApiExplorerSettings.GroupName is only for the logical group name used in API descriptions. It is not used or evaluated in route templates. There is no way to make that work. That is why it never expands. It is possible to use a {groupName} route parameter, but it will be just that - a route parameter.
It's not entirely clear if you are trying to control grouping for the API Explorer or use a route parameter. Grouping is typically used by the API Explorer. API Versioning will assign or override the group name so that APIs are bucketized by their version. It is possible to change this behavior, but you'll need an API Explorer (via IApiDescriptionProvider) or OpenAPI/Swagger document generator extension to do it.

Related

Where does this "Required" attribute for the Swagger UI coming from for ASP.NET?

I have the following in my ASP.NET Controller:
[Produces("application/json")]
[Consumes("application/json")]
[Route("api/[controller]")]
[ApiController]
public class ConnectionManagersController : ControllerBase
{
[HttpGet("{connectionManagerID:int}")]
[ProducesResponseType(typeof(ConnectionManagerModel), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ConnectionManagerModel>> GetConnectionManagerAsync(int connectionManagerdID){}
}
However when I run the app and the Swagger UI comes up I get the following screen:
There are TWO connectionManagerID fields on the Swagger UI - the first is an int (which is should be) and the second is a string and is required which I dont know where that is coming from.
I dont know where the *required field is coming from.
The parameter connectionManagerdID is an int - it is not nullable so in effect it is required.
Try this:
public async Task<ActionResult<ConnectionManagerModel>> GetConnectionManagerAsync(int connectionManagerdID = null){}
It should set it to optional.
it is from here, you will probably get 404, if you don't supply id to url.
[HttpGet("{connectionManagerID:int}")]
and if you don't want to be required try this
[HttpGet("{connectionManagerID?:int}")]
in this case maybe it makes sense to change the action too
public async Task<ActionResult<ConnectionManagerModel>> GetConnectionManagerAsync(int? connectionManagerdID){}
or you can keep what you have. It will be 0 by default.
I had the two parameters spelled differently. :(

Injecting IOptions<> into ApiKeyAuthorizeAttribute

I am using options pattern that stores different configurations, including API keys for different environments. So far I have been using it fine and injecting my values into classes as needed.
However, I faced a little challenge while trying to setup authorization in the controller and run validation against my ApiKey that is unique per environment, because I was not able to inject IOptions into ApiKeyAuthorizeAttribute class to perform validation.
Here is how my controller looks like now:
[ApiKeyAuthorize]
public class NotificationSettingsController : Controller
{
//some endpoints here
}
ApiKeyAuthorize Class:
public class ApiKeyAuthorizeAttribute : Attribute, IAuthorizationFilter
{
//////This...
private readonly IOptions<MyConfig> _config;
public ApiKeyAuthorizeAttribute(IOptions<MyConfig> config)
{
_config = config;
}
/////////...is what I am trying to accomplish
public void OnAuthorization(AuthorizationFilterContext context)
{
var request = context.HttpContext.Request;
var foundApiKeys = request.Headers.TryGetValue("ReplaceWithOptionsApiKeyName", out var requestApiKeys);
if (!foundApiKeys || requestApiKeys[0] != "ReplaceWithOptionsApiKeyValue")
{
context.Result = new UnauthorizedResult();
}
}
}
My problem is that injecting here isn't possible, but I need to get a value from IOptions<> to run ApiKey validation.
Attributes are constructed in-place, so it's not possible to inject dependencies into them. However, ASP.NET Core provides a workaround. Instead of applying the attribute directly, you can use the ServiceFilter attribute instead and pass it the type of the filter you want to apply:
[ServiceFilter(typeof(ApiAuthorizeAttribute))]
This will dynamically apply the filter to the controller/action while instantiating it with any dependencies it requires at the same time. However, it does limit you in the other direction. For example, if you need to do something like:
[ApiAuthorizeAttribute(Roles = "Admin")]
It would not be possible to achieve this with the ServiceFilter attribute, because you cannot pass property values, like Roles here, along with the type.

How can I use "For" Keyword in IActionResult name of .net core?

As google suggest in this page:
https://support.google.com/webmasters/answer/7451184?hl=en
Simple URLs should convey content information
My product name is tools for car,so my url is http://localhost:4413/Product/tools-for-car
Now the problem is in the Controller,when I use this name in controller,Visual Studio report error for the keyword "For" as below:
I wonder if there is a way that I can use this url name?Or I can only change the name.Thank you.
What you probably want is to specify the routes explicitly:
[Route("[controller]")]
public class ProductController : Controller
{
[Route("tools-for-car")]
public IActionResult CarTools() { … }
}
See the Routing to controller actions documentation for more detail.

What's causing the parameter to be null?

I am using Asp.Net Core 2.0 and web api to build a rest service. All works fine except for HTTPPost.
[HttpPost("LoginUser")]
public IActionResult LoginUser(LoginUser loginUser)
{
return Ok(loginUser);
}
loginUser is always null. I am testing with fiddler and my route is http://localhost:53250/api/User/LoginUser
and the body is
{"EmailAddress":"xx#xx.com","Password":"123456789"}
Fiddler hits the link just fine, but payload is always null.
I have also tried
[HttpPost("LoginUser")]
public IActionResult LoginUser([FromBody] LoginUser loginUser)
{
return Ok(loginUser);
}
In this case, it doesn't hit the function.
This is the LoginUser definition:
public class LoginUser
{
public string EmailAddress { get; set; }
public string Password { get; set; }
}
Any Ideas?
Your action should be:
[Route("api/[controller]")]
public class UserController : Controller
{
[HttpPost("LoginUser")]
public IActionResult LoginUser([FromBody] LoginUser loginUser)
{
return Ok(loginUser);
}
}
See, [HttpPost("LoginUser")] affects only route and doesn't relate to LoginUser object type.
Update: you need [FromBody] as ASP.NET Core model binding by default looks into [FromForm] binding source. And [FromBody] attribute indicates that you want to bind a parameter to data in the request body.
Update 2: you also should add Content-Type: application/json header to request. ASP.NET Core selects input formatters based on the this header.
Update 3: if you really need to get body data as raw string, look into ASP.NET Core MVC : How to get raw JSON bound to a string without a type?. It suggests using [FromBody] dynamic data
JSON parsing is case sensitive. Your JSON is in the wrong case.
Should be: {"EmailAddress":"xx#xx.com","Password":"123456789"}.
Issue has been solved. When I added my UserController, I did so as a class and derived from controller. I deleted it and added it as a new item and picked web api core controller. Now all is working just fine. Thanks for your help.
If you have properties in your request model that are set to {get;
private set;}, the values will not get populated. Make them public by removing private. Also constructors
aren't utilized.
If you're reading plain text from the body, see if [FromForm]
works.

Web Api routing with parameter option?

I would like to provide a ASP.Net Web API that can be called by the following formats:
http
://myApiServer/API/MyLookupMethod/GetForIVR/PhoneNumber/8005551212
or
http
://myApiServer/API/MyLookupMethod/GetForIVR?LookupType=PhoneNumber&LookUpValue=8005551212
Is it possible to set up a route that works with either call?
My current route is
config.Routes.MapHttpRoute(
name:"MyRoute",
routeTemplate:"api/{controller}/{action}/{Lookuptype}/{lookupvalue}"
);
Try to use attribute routing
[Route("api/user/YourMethodName/{id}/{param2}")]
[HttpGet]
public HttpResponseMessage YourMethodName(int id = 0, bool param2= true)
Hope it works!!
You can use Attribute based routing i.e. specific routing on controllers and methods. Further you should pass your 2 parameters as query-string like in this URL MyLookupMethod/GetForIVR?LookupType=PhoneNumber&LookUpValue=8005551212 and they will be automatically parsed as method parameters.
public class SomeController : ApiController
{
// GET MyLookupMethod/GetForIVR?LookupType=PhoneNumber&LookUpValue
[Route("MyLookupMethod/GetForIVR")]
public Book Get(int LookupType, int LookUpValue) { ... }
}