Jaxrs QueryParam as a sub or a root item - jboss

Hi the design document I'm working off of wants me to have a URL in the pattern of
<root>/v1/installs/XYZ123/actions/next?app=1234ABCD HTTP/1.1
However the only examples I can find are extremely simple, and only show URLs that would end at the /next.
http://www.mkyong.com/webservices/jax-rs/jax-rs-queryparam-example/
I'm thinking it's something like... ? I'm missing a key step
#GET
#Produces({ "application/json" })
#Path("v1/installs/{id}/actions<SOMETHINGHERE?>/next HTTP/1.1")
public Response getSetupCommands(#PathParam("id")
String id,#QueryParam("next") String next) {
I'm using jboss and jaxrs

Why do I get the feeling that HTTP/1.1 should not be a part of the URL. You may have read/understood the design document incorrectly. If it was saying that the request should look like
GET /v1/installs/XYZ123/actions/next?app=1234ABCD HTTP/1.1
Then you only need to be worried about /v1/installs/XYZ123/actions/next?app=1234ABCD. HTTP/1.1 is simply the HTTP version that will be used implicitly with every request and response.
Your original example was fine, exception you should have replaced #QueryParam("next") with #QueryParam("app"). next is actually part of the path.
This /v1/installs/{id}/actions/next should be what's included in #Path.
The complete semantics of this request URL seems to read something like:
Get the next (resource) controller, and we will use the app query parameter as an argument to pass to this controller.
UPDATE: with example
#Path("/v1")
public class QueryResource {
#GET
#Path("/installs/{id}/actions/next")
public Response getResponse(#PathParam("id") String id,
#QueryParam("app") String app) {
StringBuilder sb = new StringBuilder();
sb.append("ID: ").append(id).append("<br/>");
sb.append("app param: ").append(app);
return Response.ok(sb.toString()).build();
}
}
Browser Test
Fire bug

v1/installs/{id}/actions/{next : .+}
public Response getSetupCommands(#PathParam("id") String id,#PathParam("next") String next) {
Way too hackish for my tastes, but I don't have any control in this situation..

Related

ASP.NET Core 6 - Use URI's extension to get Accept header value

I'm migrating an application from NancyFx to Kestrel in ASP.NET Core 6.
In Nancy, you could specify the Accept value in the URI. For example, these Uris:
http://localhost:5000/my/resource.json
http://localhost:5000/my/resource.protobuf
http://localhost:5000/my/resource.xml
Would be the equivalent of setting the Accepts header to application/json, application/protobuf or application/xml respectively.
Does this exist in Kestrel? I remember finding one example, long ago, of regex-ing the route and doing it somewhat manually. But
I can't find that post again, and
If I have to do that, I'm not sure I want to :)
Is there a way to configure this behavior in ASP.NET Core 6?
The object returned from my handler in the controller is already capable of being serialized to json/xml/whatever. I just need to check the URI to set the content-type of the response so the correct formatter will be invoked.
At the moment, I have a client that will speak to both Nancy and Kestrel and it was written to use the URI to get the type. I'm fine to rewrite/update the client so it will use the Accept header. But getting the URI method to work will make the initial integration easier and a refactor to use the headers can come next.
I created a very simple middleware that reads the accept value from the query string and sets the Accept header to the request:
public class AcceptHeaderFromQueryString
{
private readonly RequestDelegate _next;
public AcceptHeaderFromQueryString(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var accept = context.Request.Query["accept"];
if (!string.IsNullOrEmpty(accept))
{
context.Request.Headers.Accept = accept;
}
await _next(context);
}
}
Register the middleware:
app.UseMiddleware<AcceptHeaderFromQueryString>();
I added [Produces(MediaTypeNames.Application.Json, MediaTypeNames.Application.Xml)] attribute to my api controller action (this step is not required):
[HttpGet]
[Produces(MediaTypeNames.Application.Json, MediaTypeNames.Application.Xml)]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
Finally I added support for xml serialization in Program.cs:
builder.Services.AddControllers()
.AddXmlDataContractSerializerFormatters();
Then I tried these urls and they both gave appropriate response:
https://localhost:7258/weatherforecast?accept=application/json
https://localhost:7258/weatherforecast?accept=application/xml
You possibly want the [Consumes] attribute. This allows you to specify a controller action that only gets called from a route of the specified content type.
Obviously this is not using the Accepts header but the content type of the request.

Wicket generate BookmarkablePageLink or Link from URL String

My final goal is to generate a go back button in my wicket site forms.
Right now I'm able to get the referrer with:
HttpServletRequest req = (HttpServletRequest)getRequest().getContainerRequest();
l.info("referer: {}", req.getHeader("referer"));
This works and I get the whole URL (as a String) but I'm unable to generate a Link object from this.
I'm not sure about the internals although I've been seeing the code for Application.addMount, IRequestHandler and more, I'm not able to find exactly where a URL is converted to what I need to generate a BookmarkablePageLink: the Class and the PageParameters.
P.S. I know this can be done with JavaScript, but I want to serve users without JS active.
Thanks
Possible solution I'm currently using:
public static WebMarkupContainer getBackButton(org.apache.wicket.request.Request request, String id) {
WebMarkupContainer l = new WebMarkupContainer(id);
HttpServletRequest req = (HttpServletRequest)request.getContainerRequest();
l.add(AttributeModifier.append("href", req.getHeader("referer")));
return l;
}
In my markup I have:
<a wicket:id="backButton">Back</a>
And then, in my Page object:
add(WicketUtils.getBackButton(getRequest(), "backButton");
If anyone has any better idea, I'm leaving this open for a while.
You should be able to use an ExternalLink for this.
Something resembling
public Component getBackButton(org.apache.wicket.request.Request request, String id) {
HttpServletRequest req = (HttpServletRequest)request.getContainerRequest();
String url = req.getHeader("referer");
return new ExternalLink(id, url, "Back");
}
with html
this body will be replaced
And your Page object code unchanged.

Restful API and SpringMVC, should I make path variables as #RequestBody object's fields for convenience?

I own an application for collecting feedback.
The api looks like this:
POST /customer/feedback/{feedbackId} HTTP/1.1
Host: localhost:9999
Content-Type: application/json
Content-Body: {'qaSays':'Approved', 'qaRating':98}
//This results the feedback (find by feedbackId) gets updated
The application is built using spring-mvc
//in controller
#RequestMapping(value="/customer/feedback/{feedbackId}")
#ResponseStatus(OK)
public void handle(#PathVariable("feedbackId") feedbackId,
#RequestBody ApproveFeedbackCommand command) {
command.setFeedbackId(feedbackId);
commandGateway.send(command);
}
//Command
public class ApproveFeedbackCommand {
private String feedbackId;//makes no sense for client
private String qaSays;
private int qaRating;
}
The solution above works, but I have a feeling that something goes wrong here.
The question is:
1.Should I use ApproveFeedbackCommand as #RequestBody just for convenience?
The extra field "feedbackId" may confuse the other developer, and it is annoying to set path variables manually.
2.Should I add an ApproveFeedbackBody(exclude "feedbackId") for separate concerns?
But this seems to add some duplicate code and some manual command parameter extraction.
#RequestMapping(value="/customer/feedback/{feedbackId}")
#ResponseStatus(OK)
public void handle(#PathVariable("feedbackId") id,
#RequestBody ApproveFeedbackBody body) {
ApproveFeedbackCommand command = convert(id, body);//manual convert here
commandGateway.send(command);
}

Play Framework redirect not working in Safari

I'm attempting to query Solr from Angular and routing the request through a Play Controller for security and using Play redirect to forward the request to Solr.
This seems to be working on Chrome but not on Safari/Firefox.
Angular ajax request
var solrUrl = '/solr';
storesFactory.getAdvancedMessages = function (searchCriteria, searchType) {
var filterQuery = solrQueryComposer(searchCriteria);
$log.warn(filterQuery);
return $http({
method: 'GET',
url: solrUrl,
params: { 'q': '*',
'fq': filterQuery,
'rows': 30,
'wt': 'json'}
}).
then(function(data, status, headers, config) {
$log.debug(data.data.response.docs);
return data.data.response.docs;
},
function(error){
$log.error(error.message);
});
Play Controller
import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.Security;
#Security.Authenticated(Secured.class)
public class SolrController extends Controller {
private static String solrUrl = "http://whatever.com:5185/solr/select/";
private static String queryPart = "";
public static Result forward(){
queryPart = request().uri().substring(5);
System.out.println(queryPart);
return seeOther(solrUrl+queryPart);
}
}
Play Route
GET /solr controllers.SolrController.forward()
First of all, I'd like to clarify what you're doing.
Play is not forwarding anything here, it's sending a redirect to the client, asking to fetch another URL. The client will send a request, receive a redirect, and send another request.
Which means:
this controller is not "forwarding" anything. It's just tells the client to go somewhere else. ("seeOther", the name speaks for itself).
It's not secure at all. Anyone knowing solr's URL could just query it directly.
since the query is performed by the client, it may be stopped by the cross-domain security policy.
Moreover, There's a HUGE race condition waiting to happen in your code. solrUrl and queryPart are static, therefore shared by all threads, therefore shared by all clients!!
There's absolutely no reason for queryPart to be static, and actually, there's absolutely no reason for it to be in this scope. This variable should be defined in the method body.
I'd also like to point out that request().uri().substring(5) is very brittle and is going to break if you change the URL in the route file.
In return seeOther(solrUrl+queryPart), queryPart arguments keys and values should also be URLencoded.

Why is Jersey's UriBuilder.build method encoding #'s and %'s, but not /'s?

I have a REST API which is fairly typical, except that the id's of resources are not integers, but strings, which often contain / characters. So if a customer's id is string/with/slashes then the URI for that customer should be http://localhost/customers/string%2Fwith%2Fslashes. When returning a list of customers, I want to construct that URI with a UriBuilder so I can put it in the href of ATOM-style link elements. But it doesn't quite work; here's a small test class that shows what I mean:
#Path("/customers")
public class JerseyTest {
#Path("{id}")
public Customer getCustomer(#PathParam("{id}") String id) {
return null;
}
public static void main(String[] args) {
buildURI("string#with#hashes"); // => http://localhost/customers/string%23with%23hashes
buildURI("string/with/slashes"); // => http://localhost/customers/string/with/slashes
}
public static void buildURI(String id) {
UriBuilder builder = UriBuilder.fromUri("http://localhost");
builder.path(JerseyTest.class).path(JerseyTest.class, "getCustomer");
URI uri = builder.build(id);
System.out.println(uri);
}
}
The #'s get encoded as I would expect but the /'s don't. I tried using builder.build(URLEncoder.encode(id)) instead, but then the UriBuilder encodes the %'s so you get .../string%252Fwith%252Fslashes!
It seems inconsistent to me that it encodes # and % but not /, but I suspect there is a good reason for it which I am not seeing. So my question is:
How can I get UriBuilder to give me .../string%2Fwith%2Fslashes, which is the URI that causes Jersey to call getCustomer with id equal to string/with/slashes? edit: I discovered a way to solve this: builder.buildFromEncoded(URLEncoder.encode(id)). Leaving this question open though, in hopes of getting an answer to the second part...
More generally, why does UriBuilder.build encode some special characters, but not others?
I found How do I encode URI parameter values?, where the accepted answer says "Use UriBuilder." Well, I am using it, but apparently I'm using it incorrectly.
This seem to be a confirmed issue:
http://java.net/jira/browse/JAX_RS_SPEC-70
Your workaround sounds good.