I have the following API Controller
[ApiController]
[Route("api/[controller]")]
public class SubContractsController: ControllerBase
{
private readonly ISubContractsRepository subContractsRepository;
public SubContractsController(ISubContractsRepository subContractsRepository)
{
this.subContractsRepository = subContractsRepository;
}
[HttpGet]
public async Task <ActionResult<IEnumerable<SubContract>>> GetSubContracts()
{
try....
I don't get result with https://localhost:7059/api/subcontracts ( full lower case) where as all the three below Urls give me the desired results. Note that S and C are capatalised differently in the below Urls.
https://localhost:7059/api/Subcontracts
https://localhost:7059/api/subContracts
https://localhost:7059/api/SubContracts
Are the routes case sensitive if so why is it working for all combinations except for one?
How do I disable the case sensitive nature?
Text matching is case-insensitive and based on the decoded
representation of the URL's path.
Read this , to know more about route.
I don't get result with https://localhost:7059/api/subcontracts ( full
lower case)
Try again, maybe something wrong. I can use lower case and all case.
Result:
I want to confirm that I've seen and wrestled this problem myself. It is real. To validate, I open with a InPrivate browser, and the API works as expected. It's not the code or .NET, it's the browser. I wanted to confirm this hear so that people can know the solution.
Related
Wondering if anybody in the community has any experience or guidance on how one could use
Authorization decorators (or any custom decoration?)(https://loopback.io/doc/en/lb4/Decorators_authorize.html) on CrudRestController endpoints? (https://loopback.io/doc/en/lb4/Creating-crud-rest-apis.html).
Looked at the src for crud-rest.controller.ts and it just seems like there is no way to really do it.
It seems like it's not easily possible to use any decoration of endpoints in a CrudRestController without taking a very hacky approach and/or wholesale duplicating the code in crud-rest.controller.ts and that we'll have to basically write every endpoint for every model by hand.
Maybe someone has come up with something or has some guidance on an approach? Is the only way to use auth with CrudRestController with the AuthorizationComponent as of now to use Authorizer functions (https://loopback.io/doc/en/lb4/Authorization-component-authorizer.html)
Seems like one part lies in this :
https://github.com/loopbackio/loopback4-example-shopping/blob/9188104c01516a5cbd4ce13f28abe18bafef821e/packages/shopping/src/services/basic.authorizor.ts
/**
* Allow access only to model owners, using route as source of truth
*
* eg. #post('/users/{userId}/orders', ...) returns `userId` as args[0]
*/
if (currentUser[securityId] === authorizationCtx.invocationContext.args[0]) {
return AuthorizationDecision.ALLOW;
}
So I ended up doing :
async authorize(
context: AuthorizationContext,
metadata: AuthorizationMetadata,
) {
const parent = context.invocationContext?.parent
const request = parent?.getBinding("rest.http.request").getValue(parent)
const givenUserId = request?.body?.userId
// next line finds out the user id in the JWT payload
const jwtUserId = context?.principals[0]?.payload?.sub
if (!jwtUserId || (givenUserId && givenUserId != jwtUserId)) {
return AuthorizationDecision.DENY;
} else {
return AuthorizationDecision.ALLOW;
}
}
as my userId is provided in the http parameters (post form or get parameters)
I also use a custom JTWService to read the payload and make it available in the UserProfile.
This may not be the best way to do it, but so far it works. I am still working on finding out how to deal with read requests and add a filter on all of them by userId too using decorators I will post my finding here, if nothing better show up first here.
I have a REST interface endpoint like
POST /items/12345/actions
I utilize a generic actions sub collection to be apply to apply changes to 12345 which are not easily mapped to the content or direct other sub collections of it.
My question is the following: Since there could be multiple different action types I identify the action by a JSON property of the content of an uploaded document.
How do I select a action by a part of the JSON body of the request. Is there something possible like...
[Route("api/v1/items")
public class ItemsController : Controller
{
[HttpPost("{id}/actions")]
[CheckJsonBody("type", "ActionA")]
public ActionResult DoActionA(int id, ActionA a)
{
// do something
}
[HttpPost("{id}/actions")]
[CheckJsonBody("type", "ActionB")]
public ActionResult DoActionB(int id, ActionB b)
{
// do something
}
}
The request would look like ...
{
"type": "ActionA",
"abc": "xyz"
}
I have digged myself up into the code till Microsoft.AspNetCore.Mvc.ActionConstraints.ActionMethodSelectorAttribute (GitHub).
However starting from there, I am a bit lost to reach a high-performance solution. Do I need to decode the body or is that something which is already done at that time the constraint is evaluated?
ps: And yes, I know I could handle them in one action and do a switch on the "type" property.
An ASP.NET team member was so friendly to direct me to an answer: In the ActionMethodSelectorAttribute you can read the body into a memory stream, read till the property for the selection filter. Then you seek the memory stream to zero and replace it in the request (for later model binding). You can cache the criteria value in HttpContext.Items to speed it up if you use the same property for multiple actions.
Is it possible to setup Web Api 2 route based on a parameter's value in the query string.
I have the following requirement:
/api/controller?data=param.data1
should go to controller's action 1
/api/controller?data=param.data2
should go to controller's action 2
any other value of data must go to action 3.
I know there's an option to set a constraint with a regex, but the examples I've found are for generic scenarios and not as specific as mine.
This is what I've tried
config.Routes.MapHttpRoute(
name: "test",
routeTemplate: "api/Hub/{data2}",
defaults: new { action = "Test" },
constraints: new { data2 = #"^(param\.data2)$" }
);
Is there a way to do it? Maybe there's a better way?
Important to note, I cannot change the URI of the service. It must have ?data=[value]
This is a fallback for a legacy system :(
You can use Attribute Routing, new in Web API 2.
Let's say you have the following actions, where the data param is, let's say, a string:
public Stuff GetStuffForData1(string data) { ... }
public Stuff GetStuffForData2(string data) { ... }
public Stuff GetStuffForData(string data) { ... }
Since you mentioned regex, you can specify route constraints for each of the above actions using a regex like the one you mentioned in your question1, for example:
[Route("controller/{data:regex(#"^(param\.data1)$")]
public Stuff GetStuffForData1(string data) { ... }
[Route("controller/{data:regex(#"^(param\.data2)$")]
public Stuff GetStuffForData2(string data) { ... }
// No need for a route constraint for other data params.
public Stuff GetStuffForData(string data) { ... }
The general syntax is {parameterName:constraint(params)} (params is optional and is not used for all constraints). In the above example, the first route will only be selected if the data segment of the URI matches the data1 regex. Similarly, the second route will be selected if the data segment of the URI matches the data2 regex. Otherwise, the last route will be chosen.
In general, the total ordering is determined as follows:
Compare the RouteOrder property of the route attribute. Lower values are evaluated first. The default order value is zero.
Look at each URI segment in the route template. For each segment, order as follows:
Literal segments.
Route parameters with constraints.
Route parameters without constraints.
Wildcard parameter segments with constraints.
Wildcard parameter segments without constraints.
In the case of a tie, routes are ordered by a case-insensitive ordinal string comparison (OrdinalIgnoreCase) of the route template.
You can even create your own custom route constraints by implementing the IHttpRouteConstraint interface and registering it in the Register method of your WebApiConfig class, assuming you're hosting on IIS, or in the Configuration method of your Startup class if self-hosting using OWIN.
Note I haven't personally tried any of the above, but it should all work; at the very least it should give you some ideas. For more details, including very nice examples, you should start by taking a look at the following article (which I shamelessly used extensively in my answer):
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#constraints
1 I'm really not an expert on writing regexes, so unfortunately I can't advise you on the specific ones you'll need.
I have two urls like this,
/products/home?subcat=abc
/products/home?subcat=xyz
I would like to have two action methods in products controller - homeabc, and homexyz.
How can I construct the route so it goes to the correct action method, depending on the value of the 'subcat' parameter?
PS: I don't have the freedom to change the url format, it has to stay the same.
Thanks.
Since you cannot change the URL formats and you only have one or two, the easiest way would be to do some type of switch in the Home/Index method
public ActionResult Index(string subcat){
switch(subcat.ToLower())[
case "abc":
return RedirectToAction("abc");
case "xyz":
return RedirectToAction("xyz");
default:
return RedirectToAction("UnknownCategory", "Errors");
}
}
Unfortunately, the routing engine does not allow you to route based on query string parameters (?). The only other possibility would be a url rewrite.
I am designing a REST API.
I have a single resource that I want to be able to change the status of for different conditions e.g. the URI is:
Applications/{application_id}/
The possible status changes are to set the application to:
Cancelled
SignedOff
Hold
Each status change will require different information e.g. a reason for cancelled, a date for signedoff.
What would be a good looking URI to handle this? I had thought of
POST: Applications/{application_id}/Cancel
POST: Applications/{application_id}/SignOff
POST: Applications/{application_id}/Hold
but it doesnt seem right to me.
EDIT:
I should have mentioned that I was already planning
POST: Applications/{application_id}
to update an existing application with a full set of application data.
I would stick with one url for all statuses and have your Status object encapsulate all the different properties. These keeps your url from having words that look like actions and to be more restful.
POST: Applications/{application_id}/status
public class Status
{
public string StatusType {get;set;}
public string CancelReason {get;set;}
public string SignOffDate {get;set;}
...
}
POST: Applications/{application_id}?cancel=true
POST is used only for CREATE. I think put will be better option.