Is "/literal/{param}/literal" a valid url template for a WCF restful service - rest

I am trying to add a new endpoint to a WCF based REST service with the following URL template:
/books/{bookId}/pdf
but it gives an error saying:
The UriTemplate '/books/*/pdf' is not valid; the wildcard ('*') cannot appear in a variable name or literal, unless as a construct for a wildcard segment. Note that a wildcard segment, either a literal or a variable, is valid only as the last path segment in the template; the wildcard can appear only once. See the documentation for UriTemplate for more details.'
Here's the service contract:
[OperationContract]
[WebInvoke(UriTemplate = "/books/{bookId}/pdf", Method = "POST")]
Message GetBookPDF(string bookId);
Is this a limitation that the variable is only valid as the last part of the url? I couldn't find any link that confirms that.

I am sure that the variable needn’t configured in the last part of the URL.
My service contract
public interface ITestService
{
[OperationContract]
[WebInvoke(Method ="POST",UriTemplate ="/books/{id}/pdf")]
string Test(string id);
}
Result.
Please have a look at this link.
https://stackoverflow.com/questions/13956889/in-wcf-can-i-have-a-wildcard-character-in-a-literal-segment-of-my-uritemplate
The most likely scenario is that the UriTemplate can match multiple OperationContract, thus the error happened.
Feel free to let me know if the problem still exists.

Related

Vertx router configuration

I am a novice with vertx so maybe I am doing something wrong. I am trying to implement the following routes:
router.get("/api/users/").handler(this::getUsers);
router.route("/api/users/:username*").handler(this::checkUsername);
router.get("/api/users/:username/").handler(this::getUser);
router.put("/api/users/:username/").handler(this::addUser);
router.get("/api/users/:username/assignments/").handler(this::getAssignments);
router.post("/api/users/:username/assignments/").handler(this::addAssignment);
router.route("/api/users/:username/assignments/:assignmentId/").handler(this::checkAssignmentId);
router.get("/api/users/:username/assignments/:assignmentId/").handler(this::getAssignment);
Is this the correct way to avoid duplicating this logic in all handlers?
I am trying to chain handlers, where the checkUsername handler reads the username parameter from the path, tries to find a corresponding user, and puts that user in the context. If no user is found, a statuscode 400 is returned. Otherwise the next handler is called. I would like to apply the same principle to the assignmentId parameter.
While trying to implement this, I believe I found a problem with the path, more specifically the trailing slash and star. The documentation states that trailing slashes are ignored. This is not the behavior when there is a parameter in the path. In that case the trailing slash matters. If the path definition contains one and the request does not, vertx returns a 404. It does not make a difference whether or not the parameter is at the end of the path or in the middle.
The same goes for paths ending with a star. This functionality does not work when the path contains a parameter.
You can use a regular expression to avoid duplication of the checkUsername validation check. What I would do is I would have a method like this to check if the username is valid:
private void checkUsername(RoutingContext routingContext){
//The "param0" is the capture group of the regular expression. See the routing config below.
if (isValidUsername(routingContext.request().getParam("param0"))){
routingContext.next();
} else {
routingContext
.response()
.setStatusCode(400)
.end();
}
}
To check the assignment ID I would do something similar:
private void checkAssignmentId(RoutingContext routingContext){
if (isValidAssignmentId(routingContext.request().getParam("assignmentId"))){
routingContext.next();
} else {
routingContext
.response()
.setStatusCode(400)
.end();
}
}
Try to avoid trailing slashes in your paths. I would change the routing handler assignments to be something like this:
router.get("/api/users").handler(this::getUsers);
//By the way, you really want to be using a POST request when adding users just to stick to the principles of REST.
//When you are sending a POST request there is no need to put the username in the URI. You can have it in the request body.
//Please ensure you validate this username using the same validation helper used in your other validations.
router.post("/api/users").handler(this::addUser);
//Use regular expression to match all "/api/users/:username*" URIs
router.routeWithRegex("\\/api\\/users\\/([^\\/]+)").handler(this::checkUsername);
router.get("/api/users/:username").handler(this::getUser);
router.get("/api/users/:username/assignments").handler(this::getAssignments);
router.post("/api/users/:username/assignments").handler(this::addAssignment);
router.route("/api/users/:username/assignments/:assignmentId").handler(this::checkAssignmentId);
router.get("/api/users/:username/assignments/:assignmentId").handler(this::getAssignment);

Allow special characters in Web API parameters

I need to be able to handle special characters in a REST call. Specifically the . and / characters.
For example I have a GET route /api/division/{someDivision}. Now, calling this route with a parameter of /api/division/West Canada/ I get a return and everything works as expected. However, I need to be able to support other business divisions which have names such as "Southwest U.S." and "North/South America". Passing these parameters through my route returns a 404 via the api, since I presume, that the http handler thinks that the . and / characters make it think I'm referring to another domain or directory. Is there anyway to work around this so I can pass the needed parameter?
The route:
[HttpGet]
[Route("{division}/information")]
public IHttpActionResult DivisionInfo(string division)
{
...omitted for brevity
You could try setting your route up like this:
[HttpGet]
[Route("/api/information")]
public IHttpActionResult DivisionInfo(string division)
Then you can call GET http://website.com/api/information?division=text.with/special./characters.
try adding [FromUri] before the param:
Route("api/Person/{ID}/[FromUri]{UserName}")]

CQ5: How to programmatically find out the Resource given a URL?

According to ResourceResolver Interface:
http://dev.day.com/docs/en/cq/current/javadoc/org/apache/sling/api/resource/ResourceResolver.html
There are three ways to resolve either path or request to a Resource:
Resource resolve(HttpServletRequest request)
Deprecated. as of 2.0.4, use resolve(HttpServletRequest, String) instead.
Resource resolve(HttpServletRequest request, String absPath)
Resolves the resource from the given absPath optionally taking
HttpServletRequest into account, such as the value of the Host request header.
Resource resolve(String absPath)
Resolves the resource from the given absolute path.
But if I have a random given URL string (e.g. http://www.mycompany.com/whatever.html), how do I programmatically find out the corresponding Resource of the given URL?
If the hostname/port from the URL is mapped to a content repository location CQ will attempt to resolve the URL supplied.
In a servlet the ResourceResolver can be obtained from the slingRequest:
ResourceResolver resourceResolver = slingRequest.getResourceResolver();
String resourcePath = new URI("http://www.mycompany.com/whatever.html").getPath();
Resource res = resourceResolver.resolve(resourcePath);
Bear in mind that for short urls and domains like the above to work you would need to configure mapping on your instance.
In a JSP, as long as you have called the <sling:defineObjects/> or <cq:defineObjects/> tag you will be able to use:
<sling:defineObjects>
<%
String resourcePath = new URI("http://www.mycompany.com/whatever.html").getPath();
Resource res = resourceResolver.resolve(resourcePath);
%>
Some more information is provided in "Getting Resources and Properties in Sling"
Test out on a couple of URLs you know are good. For example:
Resource res = resourceResolver.resolve("http://localhost:4502/content/geometrixx.html");
Resource res = resourceResolver.resolve("/content/geometrixx.html");
Both of the above should resolve to the same resource.
If you want to test whether CQ can resolve the URL you are providing, try the jcr resolver page in the system console
http://localhost:4502/system/console/jcrresolver to see if the url is mapped if it does not contain the full /content/.. in the path. Any mapped should be able to be resolved.
ResourceResolver class was implemented to return Resource. Specifically, the resolve() functions exist for this type of resolution. However, even there are three overloaded resolve() functions, none of them takes in a URL String.
Given ResourceResolver takes in HttpServletRequest as input, if I can transform (adapt) a given URL into a HttpServletRequest using HttpServletRequestWrapper, the problem will be solved. Therefore, the solution is to implement a ResolverRequest class which extends HttpServletRequestWrapper.
For the complete solution and code sample please see "Programmatically find the Sling Resource from a Given a URL"

What is a REST response, what does it do?

I had made a REST webservice using redirecting to various paths like if i need to delete some user then i will redirect the user to this address in the #Path annotation :
user/delete
and therefore there is no thing like RESPONSE i have used.
While going through a code given to me by my senior i came accross these lines :
java.net.URI uri = uriInfo.getAbsolutePathBuilder().path(id).build();
Response.created(uri).build();
What are these lines doing, i have no idea.
Can someone explain me this w/o wiki links or any other 'Basic Rest Service' links.
Without any explicit details about the uriInfo object I can only speculate its type is the JAX-RS UriInfo class.
The first line can be broken down as below:
java.net.URI uri = uriInfo.getAbsolutePathBuilder().path(id).build();
The getAbsolutePathBuilder is documented http://jackson.codehaus.org/javadoc/jax-rs/1.0/javax/ws/rs/core/UriInfo.html#getAbsolutePathBuilder%28%29
java.net.URI uri = uriInfo.getAbsolutePathBuilder().path(id).build();
The method returns a UriBuilder object. On which the 'path(...)' method is called passing the id so if the absolute path returned http://www.host.com (this may or may not have a port number) adding the id in this method will then result in the effectively Builder holding the two parts. The base URI and the path. The two values have not yet been put together
The build method then concatenates the two values resulting a full URI. For example http://www.google.com/id (Where http://www.google.com is the absolute path)
The second line
Response.created(uri).build();
Is basically saying 'Respond with a created (201) response code, and set a Location header containing the build uri value'

Url as path parameter in restful api causes bad request

We are developing a restful api using jersey (1.9.1) and tomcat 5.5.
A given resource is identified with a urn and we would like to address a specific instance of that resource. In order to achieve this, we used the following code:
#Path("/XXXs")
public interface XXXResource {
#GET
#Path("{id}")
#Produces({ MediaType.APPLICATION_JSON })
XXXInfo getXXX(#PathParam("id") String id);
}
The idea is to address this resource using the following url:
http://localhost:8080/restapi/XXXs/http%3A%2F%2Fns.something.com%2FXXX%2F2
The decoded path param value should be:
http://ns.something.com/XXX/2
However, when I make the request using the encoded url I get a bad request message from tomcat. So my questions are:
Is it correct to use a Urn as a path parameter?
Why is tomcat considering this request as a bad request?
Just in case, I changed the signature of the method so that the parameter is taken from the query string and it worked fine, but I want the parameter to be part of the path.
Thanks.
Ok, I solved it by adding the following line in catalina.properties:
org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true