Is there with Spring (boot) a way to check if a REST request contains a parameter not explicitly declared by the called REST method?
With the required flag we can force the client to include a certain parameter in the request. I am looking for a similar way to disallow the client to send a parameter that is not explicity mentioned in the declaration of the controller method:
#RequestMapping("/hello")
public String hello(#RequestParam(value = "name") String name) {
//throw an exception if a REST client calls this method and
// sends a parameter with a name other than "name"
//otherwise run this method's logic
}
For example calling
curl "localhost:8080/hello?name=world&city=London"
should result in a 4xx answer.
One option would be to explicitly check for unexpected parameters:
#RequestMapping("/hello")
public String hello(#RequestParam Map<String,String> allRequestParams) {
//throw an exception if allRequestParams contains a key that we cannot process here
//otherwise run this method's logic
}
But is it also possible to achieve the same result while keeping the same convenient #RequestParam usage as in the first example?
EDIT: Sorry, I do not see any connection to this question. The other question is about annotation processing at runtime. My question is about the behaviour of Spring's REST engine. Am I missing something?
EDIT: Based on the answers, I have written this HandlerInterceptor:
#Component
public class TooManyParamatersHandlerInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod m = (HandlerMethod) handler;
if (m.getMethod().getName().equals("error")) {
return true;
}
List<String> allowedParameters = Stream.of(m.getMethodParameters())
.flatMap(p -> Stream.of(p.getParameterAnnotation(RequestParam.class)))
.filter(Objects::nonNull)
.map(RequestParam::name).collect(Collectors.toList());
ArrayList<String> actualParameters = Collections.list(request.getParameterNames());
actualParameters.removeAll(allowedParameters);
if (!actualParameters.isEmpty()) {
throw new org.springframework.web.bind.ServletRequestBindingException(
"unexpected parameter: " + actualParameters);
}
return true;
}
}
In this case you required HandlerInterceptor or HandlerInterceptorAdapter, override the preHandle method
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//request param validation validation
return true; //or throw exception
}
ServletRequest.getParameterMap() returns a map of key-values of the request parameters.
You can do it by ContainerRequestFilter feature which is added from JavaEE 7 that lets you access the resource class and resource method matched by the current request and make you to do your desire action when that have not been matched.
You can read more here :
https://docs.oracle.com/javaee/7/api/javax/ws/rs/container/ResourceInfo.html
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.QueryParam;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.Provider;
#Provider
public class RequestParamFilter implements ContainerRequestFilter {
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
Set<String> acceptedParamList = new HashSet<String>();
Method method = resourceInfo.getResourceMethod();
for (Annotation[] annos : method.getParameterAnnotations()) {
for (Annotation anno : annos) {
if (anno instanceof QueryParam) {
acceptedParamList.add(((QueryParam) anno).value());
}
}
}
MultivaluedMap<String, String> queryParams = requestContext.getUriInfo().getQueryParameters();
for (String param : queryParams .keySet()) {
if (!acceptedParamList.contains(param)) {
requestContext.abortWith(Response.status(Status.BAD_REQUEST).entity("Unexpected paramter found : "+param).build());
}
}
}
}
P.N : Filters are cost in your application speed most of the times, Specially if you have complex chains in it!
I recommend to use it in this case (and similar cases) because of most of the those requests should not be reached to the server application at all.
I hope this helps you and Happy coding! =)
As far as I know, you cannot simply disallow parameters using Spring. Honestly, this issue is rather questionable and unnecessary and I think it's an antipattern.
However, Spring provides with each mapping the HttpServletRequest and HttpServletResponse objects to the controller method signature. Use the method HttpServletRequest::getParameterMap to receive the Map of the passed parameters for the further iteration and validation.
#RequestMapping("/hello")
public String hello(RequestParam(value = "name") String name, HttpServletRequest request, HttpServletResponse response) {
final Map<String, String[]> parameterMap = request.getParameterMap();
// logics
}
Passing those object to only to the #RequestMapping("/hello") allows performing the validation only to the selected mapping. If you want to define this behavior globally, I suggest you use HandlerInterceptor::preHandle as answered here.
If you make the hello parameter required=true, then you can just check the size of the Map whether is equal to 1 or not.
Related
Since after 2 days I still cannot figure how to perform a print of a HttpServletResponse body in HandlerInterceptorAdapter, I'll ask another time :)
With HttpServletRequest I can easily do something like request.getReader().lines().collect(Collectors.joining(System.lineSeparator())); and I have the full body but how to make the same with HttpServletResponse?
I Had found lots of question on StackOverflow about that but none of them seems to work.
This is the handler:
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
//how to print here the "response" by using the "response" parameter
super.afterCompletion(request, response, handler, ex);
}
this answer is quite the same and links to this but they use ServletResponse and NOT HttpServletResponse and something with FilterChain which I have not in my afterCompletion handler. Even this that seems the most complete one is not suitable (I think) in my case.
Do someone can provide me a simple serialization example with HttpServletResponse?
It's been hard on searching deeply into it but found that ResponseBodyAdvice could be suitable for my purposes. So looking for some example on StackOverflow found this guy which had quite same issue having to manipulate the Object body.
That's my final working solution in order to implement what I wrote here
#ControllerAdvice
public class CSRFHandler implements ResponseBodyAdvice<Object> {
#Value("${security.csrf.enabled}")
private String csrfEnabled;
#Value("${security.csrf.headerName}")
private String csrfHeaderName;
#Value("${security.csrf.salt}")
private String salt;
#Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
#Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
if (new Boolean(csrfEnabled).booleanValue()) {
String csrfValue = SecureUtil.buildCsrfValue(salt, StringUtil.toJson(body));
response.getHeaders().add(csrfHeaderName, csrfValue);
}
return body;
}
}
Simple answer is "you can't do that in a Handler Interceptor".
Says so in the manual :
HandlerInterceptor is basically similar to a Servlet Filter, but in contrast to the latter it just allows custom pre-processing with the option of prohibiting the execution of the handler itself, and custom post-processing. Filters are more powerful, for example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web.xml, a HandlerInterceptor in the application context.
As a basic guideline, fine-grained handler-related preprocessing tasks are candidates for HandlerInterceptor implementations, especially factored-out common handler code and authorization checks. On the other hand, a Filter is well-suited for request content and view content handling, like multipart forms and GZIP compression. This typically shows when one needs to map the filter to certain content types (e.g. images), or to all requests.
So I advise you check out filter based solutions, as you pointed. You might be interested in : ContentCachingResponseWrapper Produces Empty Response which seems to accomplish what you want with minimal coding. But once you get started with filters, any of the well accepted answers that you have linked to in the question will likely do the job.
I ended up with the following in Kotlin:
#Bean
open fun logFilter(): CommonsRequestLoggingFilter {
val filter = InfoRequestLoggingFilter()
filter.setIncludeQueryString(true)
filter.setIncludePayload(true)
filter.setMaxPayloadLength(10000)
filter.isIncludeHeaders = false
return filter
}
import org.apache.commons.io.output.TeeOutputStream
import org.springframework.mock.web.DelegatingServletOutputStream
import org.springframework.web.filter.CommonsRequestLoggingFilter
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.PrintStream
import java.io.PrintWriter
import javax.servlet.FilterChain
import javax.servlet.ServletOutputStream
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpServletResponseWrapper
class InfoRequestLoggingFilter : CommonsRequestLoggingFilter() {
override fun beforeRequest(request: HttpServletRequest, message: String) {
logger.info(message)
}
override fun afterRequest(request: HttpServletRequest, message: String) {
// logger.info(message) - NOP, since doFilterInternal is logging it instead
}
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
val outputStream = ByteArrayOutputStream()
val printStream = PrintStream(outputStream)
filterChain.doFilter(request, object : HttpServletResponseWrapper(response) {
#Throws(IOException::class)
override fun getOutputStream(): ServletOutputStream {
return DelegatingServletOutputStream(TeeOutputStream(super.getOutputStream(), printStream)
)
}
#Throws(IOException::class)
override fun getWriter(): PrintWriter {
return PrintWriter(DelegatingServletOutputStream(TeeOutputStream(super.getOutputStream(), printStream))
)
}
})
logger.info(String.format("%s ;status=%s ;payload=%s", createMessage(request, "", ""), response.status, outputStream.toString()))
}
}
I'm writing a REST web app (NetBeans 6.9, JAX-RS, TopLink Essentials) and trying to return JSON and HTTP status code. I have code ready and working that returns JSON when the HTTP GET method is called from the client. Essentially:
#Path("get/id")
#GET
#Produces("application/json")
public M_機械 getMachineToUpdate(#PathParam("id") String id) {
// some code to return JSON ...
return myJson;
}
But I also want to return an HTTP status code (500, 200, 204, etc.) along with the JSON data.
I tried to use HttpServletResponse:
response.sendError("error message", 500);
But this made the browser think it's a "real" 500 so the output web page was a regular HTTP 500 error page.
I want to return an HTTP status code so that my client-side JavaScript can handle some logic depending on it (to e.g. display the error code and message on an HTML page). Is this possible or should HTTP status codes not be used for such thing?
Here's an example:
#GET
#Path("retrieve/{uuid}")
public Response retrieveSomething(#PathParam("uuid") String uuid) {
if(uuid == null || uuid.trim().length() == 0) {
return Response.serverError().entity("UUID cannot be blank").build();
}
Entity entity = service.getById(uuid);
if(entity == null) {
return Response.status(Response.Status.NOT_FOUND).entity("Entity not found for UUID: " + uuid).build();
}
String json = //convert entity to json
return Response.ok(json, MediaType.APPLICATION_JSON).build();
}
Take a look at the Response class.
Note that you should always specify a content type, especially if you are passing multiple content types, but if every message will be represented as JSON, you can just annotate the method with #Produces("application/json")
There are several use cases for setting HTTP status codes in a REST web service, and at least one was not sufficiently documented in the existing answers (i.e. when you are using auto-magical JSON/XML serialization using JAXB, and you want to return an object to be serialized, but also a status code different than the default 200).
So let me try and enumerate the different use cases and the solutions for each one:
1. Error code (500, 404,...)
The most common use case when you want to return a status code different than 200 OK is when an error occurs.
For example:
an entity is requested but it doesn't exist (404)
the request is semantically incorrect (400)
the user is not authorized (401)
there is a problem with the database connection (500)
etc..
a) Throw an exception
In that case, I think that the cleanest way to handle the problem is to throw an exception. This exception will be handled by an ExceptionMapper, that will translate the exception into a response with the appropriate error code.
You can use the default ExceptionMapper that comes pre-configured with Jersey (and I guess it's the same with other implementations) and throw any of the existing sub-classes of javax.ws.rs.WebApplicationException. These are pre-defined exception types that are pre-mapped to different error codes, for example:
BadRequestException (400)
InternalServerErrorException (500)
NotFoundException (404)
Etc. You can find the list here: API
Alternatively, you can define your own custom exceptions and ExceptionMapper classes, and add these mappers to Jersey by the mean of the #Provider annotation (source of this example):
public class MyApplicationException extends Exception implements Serializable
{
private static final long serialVersionUID = 1L;
public MyApplicationException() {
super();
}
public MyApplicationException(String msg) {
super(msg);
}
public MyApplicationException(String msg, Exception e) {
super(msg, e);
}
}
Provider :
#Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
#Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Note: you can also write ExceptionMappers for existing exception types that you use.
b) Use the Response builder
Another way to set a status code is to use a Response builder to build a response with the intended code.
In that case, your method's return type must be javax.ws.rs.core.Response. This is described in various other responses such as hisdrewness' accepted answer and looks like this :
#GET
#Path("myresource({id}")
public Response retrieveSomething(#PathParam("id") String id) {
...
Entity entity = service.getById(uuid);
if(entity == null) {
return Response.status(Response.Status.NOT_FOUND).entity("Resource not found for ID: " + uuid).build();
}
...
}
2. Success, but not 200
Another case when you want to set the return status is when the operation was successful, but you want to return a success code different than 200, along with the content that you return in the body.
A frequent use case is when you create a new entity (POST request) and want to return info about this new entity or maybe the entity itself, together with a 201 Created status code.
One approach is to use the response object just like described above and set the body of the request yourself. However, by doing this you loose the ability to use the automatic serialization to XML or JSON provided by JAXB.
This is the original method returning an entity object that will be serialized to JSON by JAXB:
#Path("/")
#POST
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
User newuser = ... do something like DB insert ...
return newuser;
}
This will return a JSON representation of the newly created user, but the return status will be 200, not 201.
Now the problem is if I want to use the Response builder to set the return code, I have to return a Response object in my method. How do I still return the User object to be serialized?
a) Set the code on the servlet response
One approach to solve this is to obtain a servlet request object and set the response code manually ourselves, like demonstrated in Garett Wilson's answer :
#Path("/")
#POST
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user, #Context final HttpServletResponse response){
User newUser = ...
//set HTTP code to "201 Created"
response.setStatus(HttpServletResponse.SC_CREATED);
try {
response.flushBuffer();
}catch(Exception e){}
return newUser;
}
The method still returns an entity object and the status code will be 201.
Note that to make it work, I had to flush the response. This is an unpleasant resurgence of low-level Servlet API code in our nice JAX_RS resource, and much worse, it causes the headers to be unmodifiable after this because they were already sent on the wire.
b) Use the response object with the entity
The best solution, in that case, is to use the Response object and set the entity to be serialized on this response object. It would be nice to make the Response object generic to indicate the type of the payload entity in that case, but is not the currently the case.
#Path("/")
#POST
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public Response addUser(User user){
User newUser = ...
return Response.created(hateoas.buildLinkUri(newUser, "entity")).entity(restResponse).build();
}
In that case, we use the created method of the Response builder class in order to set the status code to 201. We pass the entity object (user) to the response via the entity() method.
The result is that the HTTP code is 401 as we wanted, and the body of the response is the exact same JSON as we had before when we just returned the User object. It also adds a location header.
The Response class has a number of builder method for different statuses (stati ?) such as :
Response.accepted()
Response.ok()
Response.noContent()
Response.notAcceptable()
NB: the hateoas object is a helper class that I developed to help generate resources URIs. You will need to come up with your own mechanism here ;)
That's about it.
I hope this lengthy response helps somebody :)
The answer by hisdrewness will work, but it modifies the whole approach to letting a provider such as Jackson+JAXB automatically convert your returned object to some output format such as JSON. Inspired by an Apache CXF post (which uses a CXF-specific class) I've found one way to set the response code that should work in any JAX-RS implementation: inject an HttpServletResponse context and manually set the response code. For example, here is how to set the response code to CREATED when appropriate.
#Path("/foos/{fooId}")
#PUT
#Consumes("application/json")
#Produces("application/json")
public Foo setFoo(#PathParam("fooID") final String fooID, final Foo foo, #Context final HttpServletResponse response)
{
//TODO store foo in persistent storage
if(itemDidNotExistBefore) //return 201 only if new object; TODO app-specific logic
{
response.setStatus(Response.Status.CREATED.getStatusCode());
}
return foo; //TODO get latest foo from storage if needed
}
Improvement: After finding another related answer, I learned that one can inject the HttpServletResponse as a member variable, even for singleton service class (at least in RESTEasy)!! This is a much better approach than polluting the API with implementation details. It would look like this:
#Context //injected response proxy supporting multiple threads
private HttpServletResponse response;
#Path("/foos/{fooId}")
#PUT
#Consumes("application/json")
#Produces("application/json")
public Foo setFoo(#PathParam("fooID") final String fooID, final Foo foo)
{
//TODO store foo in persistent storage
if(itemDidNotExistBefore) //return 201 only if new object; TODO app-specific logic
{
response.setStatus(Response.Status.CREATED.getStatusCode());
}
return foo; //TODO get latest foo from storage if needed
}
If you like to keep your resource layer clean of Response objects, then I recommend you use #NameBinding and binding to implementations of ContainerResponseFilter.
Here's the meat of the annotation:
package my.webservice.annotations.status;
import javax.ws.rs.NameBinding;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
public #interface Status {
int CREATED = 201;
int value();
}
Here's the meat of the filter:
package my.webservice.interceptors.status;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
#Provider
public class StatusFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException {
if (containerResponseContext.getStatus() == 200) {
for (Annotation annotation : containerResponseContext.getEntityAnnotations()) {
if(annotation instanceof Status){
containerResponseContext.setStatus(((Status) annotation).value());
break;
}
}
}
}
}
And then the implementation on your resource simply becomes:
package my.webservice.resources;
import my.webservice.annotations.status.StatusCreated;
import javax.ws.rs.*;
#Path("/my-resource-path")
public class MyResource{
#POST
#Status(Status.CREATED)
public boolean create(){
return true;
}
}
I found it very useful to build also a json message with repeated code, like this:
#POST
#Consumes("application/json")
#Produces("application/json")
public Response authUser(JsonObject authData) {
String email = authData.getString("email");
String password = authData.getString("password");
JSONObject json = new JSONObject();
if (email.equalsIgnoreCase(user.getEmail()) && password.equalsIgnoreCase(user.getPassword())) {
json.put("status", "success");
json.put("code", Response.Status.OK.getStatusCode());
json.put("message", "User " + authData.getString("email") + " authenticated.");
return Response.ok(json.toString()).build();
} else {
json.put("status", "error");
json.put("code", Response.Status.NOT_FOUND.getStatusCode());
json.put("message", "User " + authData.getString("email") + " not found.");
return Response.status(Response.Status.NOT_FOUND).entity(json.toString()).build();
}
}
In case you want to change the status code because of an exception, with JAX-RS 2.0 you can implement an ExceptionMapper like this. This handles this kind of exception for the whole app.
#Provider
public class UnauthorizedExceptionMapper implements ExceptionMapper<EJBAccessException> {
#Override
public Response toResponse(EJBAccessException exception) {
return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build();
}
}
If your WS-RS needs raise an error why not just use the WebApplicationException?
#GET
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
#Path("{id}")
public MyEntity getFoo(#PathParam("id") long id, #QueryParam("lang")long idLanguage) {
if (idLanguage== 0){
// No URL parameter idLanguage was sent
ResponseBuilder builder = Response.status(Response.Status.BAD_REQUEST);
builder.entity("Missing idLanguage parameter on request");
Response response = builder.build();
throw new WebApplicationException(response);
}
... //other stuff to return my entity
return myEntity;
}
JAX-RS has support for standard/custom HTTP codes. See ResponseBuilder and ResponseStatus, for example:
http://jackson.codehaus.org/javadoc/jax-rs/1.0/javax/ws/rs/core/Response.ResponseBuilder.html#status%28javax.ws.rs.core.Response.Status%29
Keep in mind that JSON information is more about the data associated with the resource/application. The HTTP codes are more about the status of the CRUD operation being requested. (at least that is how it's supposed to be in REST-ful systems)
Please look at the example here, it best illustrates the problem and how it is solved in the latest (2.3.1) version of Jersey.
https://jersey.java.net/documentation/latest/representations.html#d0e3586
It basically involves defining a custom Exception and keeping the return type as the entity. When there is an error, the exception is thrown, otherwise, you return the POJO.
I'm not using JAX-RS, but I've got a similar scenario where I use:
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
Also, notice that by default Jersey will override the response body in case of an http code 400 or more.
In order to get your specified entity as the response body, try to add the following init-param to your Jersey in your web.xml configuration file :
<init-param>
<!-- used to overwrite default 4xx state pages -->
<param-name>jersey.config.server.response.setStatusOverSendError</param-name>
<param-value>true</param-value>
</init-param>
The following code worked for me. Injecting the messageContext via annotated setter and setting the status code in my "add" method.
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import org.apache.cxf.jaxrs.ext.MessageContext;
public class FlightReservationService {
MessageContext messageContext;
private final Map<Long, FlightReservation> flightReservations = new HashMap<>();
#Context
public void setMessageContext(MessageContext messageContext) {
this.messageContext = messageContext;
}
#Override
public Collection<FlightReservation> list() {
return flightReservations.values();
}
#Path("/{id}")
#Produces("application/json")
#GET
public FlightReservation get(Long id) {
return flightReservations.get(id);
}
#Path("/")
#Consumes("application/json")
#Produces("application/json")
#POST
public void add(FlightReservation booking) {
messageContext.getHttpServletResponse().setStatus(Response.Status.CREATED.getStatusCode());
flightReservations.put(booking.getId(), booking);
}
#Path("/")
#Consumes("application/json")
#PUT
public void update(FlightReservation booking) {
flightReservations.remove(booking.getId());
flightReservations.put(booking.getId(), booking);
}
#Path("/{id}")
#DELETE
public void remove(Long id) {
flightReservations.remove(id);
}
}
Expanding on the answer of Nthalk with Microprofile OpenAPI you can align the return code with your documentation using #APIResponse annotation.
This allows tagging a JAX-RS method like
#GET
#APIResponse(responseCode = "204")
public Resource getResource(ResourceRequest request)
You can parse this standardized annotation with a ContainerResponseFilter
#Provider
public class StatusFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
if (responseContext.getStatus() == 200) {
for (final var annotation : responseContext.getEntityAnnotations()) {
if (annotation instanceof APIResponse response) {
final var rawCode = response.responseCode();
final var statusCode = Integer.parseInt(rawCode);
responseContext.setStatus(statusCode);
}
}
}
}
}
A caveat occurs when you put multiple annotations on your method like
#APIResponse(responseCode = "201", description = "first use case")
#APIResponse(responseCode = "204", description = "because you can")
public Resource getResource(ResourceRequest request)
I'm using jersey 2.0 with message body readers and writers. I had my method return type as a specific entity which was also used in the implementation of the message body writer and i was returning the same pojo, a SkuListDTO.
#GET
#Consumes({"application/xml", "application/json"})
#Produces({"application/xml", "application/json"})
#Path("/skuResync")
public SkuResultListDTO getSkuData()
....
return SkuResultListDTO;
all i changed was this, I left the writer implementation alone and it still worked.
public Response getSkuData()
...
return Response.status(Response.Status.FORBIDDEN).entity(dfCoreResultListDTO).build();
I am working on a messy Struts 1 application that makes use of a custom context class to store values throughout the application. Basically it is only used to store session scope variables. I guess the reason that this custom class is used is so that other classes which do not have access to the http session can still get and set the session variables.
Anyways, for the most part this works just fine. The custom context is used throughout the Actions and service classes to share variables with no problem. However, I just discovered that things do not work out so nicely using this custom context inside of an Http Filter! It appears that it randomly will pull the value from a different session. And by session, I actually mean thread, since this custom context uses ThreadLocale to do it's dirty work.
Take a look
package com.zero.alpha.common;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
public final class CustomContext implements Serializable {
private static final long serialVersionUID = 400312938676062620L;
private static ThreadLocal<CustomContext> local = new ThreadLocal() {
protected CustomContext initialValue() {
return new CustomContext("0", "0", Locale.getDefault());
}
};
private String dscId;
private String sessionId;
private Locale locale;
private Map<String, Serializable> generalArea;
public CustomContext(String dscId, String sessionId, Locale locale) {
this.dscId = dscId;
this.sessionId = sessionId;
if (locale != null) {
this.locale = locale;
} else {
this.locale = Locale.getDefault();
}
this.generalArea = new Hashtable();
}
public static CustomContext get() {
return ((CustomContext) local.get());
}
public static void set(CustomContext context) {
local.set(context);
}
public String getDscId() {
return this.dscId;
}
public String getSessionId() {
return this.sessionId;
}
public Locale getLocale() {
return this.locale;
}
public Serializable getGeneralArea(String key) {
return ((Serializable) this.generalArea.get(key));
}
public Serializable putGeneralArea(String key, Serializable value) {
return ((Serializable) this.generalArea.put(key, value));
}
public void clearGeneralArea() {
this.generalArea.clear();
}
public Serializable removeGeneralArea(String key) {
return ((Serializable) this.generalArea.remove(key));
}
}
Again, this seems to work just fine and dandy inside every other class other than a filter. Let me show you the filter where it messes up.
package com.zero.alpha.myapp.common.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.zero.alpha.common.CustomContext;
import com.zero.alpha.myapp.utility.CommonConstants;
import com.zero.alpha.myapp.utility.CommonHelpers;
import com.zero.alpha.myapp.UserDomain;
public class LoginFilter implements Filter {
public LoginFilter() {
}
public void init(FilterConfig config) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// Don't use the login filter during a login or logout request
if (req.getServletPath().equals("/login.do")
|| req.getServletPath().equals("/login-submit.do")
|| req.getServletPath().equals("/logout.do")) {
chain.doFilter(request, response);
} else {
doFilter(req, (HttpServletResponse) response, chain);
}
}
protected void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpSession session = request.getSession(false);
// This is the problem right here. Sometimes this will grab the value of a different user currently logged in
UserDomain user = (UserDomain) CustomContext.get()
.getGeneralArea(CommonConstants.ContextKey.USER_SESSION);
if (session == null || user == null) {
// Unauthorized
response.sendRedirect(loginPage);
} else {
// Authorized
session.setAttribute("userInfo", CommonHelpers.getUserDisplay(user));
chain.doFilter(request, response);
}
}
}
When the custom context is used to grab the user in the doFilter method, it will randomly grab the user object from another logged in user. Obviously not a good situation!
The only time this happens is after some activity from a different logged in user. I could sit there all day and keep refreshing user A's session and there wouldn't be an issue. However, after taking some action as user B and then refreshing user A's session again, it will usually be swapped. But then if I refresh user A's session again, things are back to normal.
I've noticed this happens extremely more frequently when the application is actually deployed to a remote development tomcat server. It still happens when running locally, but not nearly as often as when deployed remotely. It happens almost 100% of the time remotely.
I have examined the session variable inside of the filter, and there doesn't appear to be an issue with the session itself. I have confirmed that the session id is still correct even when the incorrect user is pulled from the ThreadLocale variable.
Can anyone help me out? Thanks.
Your strategy is irretrievably flawed. Unless your servlet engine is single-threaded, you cannot rely on the same thread to handle every request in a given session. Moreover, and more relevant to the problem stated, even when the same thread to handle one request in a given session is also used to handle the next request in that session, it is not safe to assume that it does not handle a request belonging to a different session in between. There is no guarantee of thread / session binding.
Anyway, you seem to be missing the obvious. If you want to store session-scope variables, then store them in the session. That's what session attributes are for. See HttpSession.setAttribute() and HttpSession.getAttribute() -- you can use these to set and retrieve your context object.
Here is my code:
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.StrutsStatics;
#SuppressWarnings("serial")
public class PostOnlyInterceptor extends AbstractInterceptor {
#Override
public String intercept(ActionInvocation ai) throws Exception {
final ActionContext context = ai.getInvocationContext();
HttpServletRequest request = (HttpServletRequest) context.get(StrutsStatics.HTTP_REQUEST);
if (!request.getMethod().equals("POST")) {
return Action.ERROR;
}
return ai.invoke();
}
}
I am using this interceptor to avoid 'GET' method requests for security reasons. But when I am calling it by using chain action method: request.getMethod() returns GET request.
So how to handle this situation?
Beware of Action Chaining, that is discouraged:
As a rule, Action Chaining is not recommended. First explore other options, such as the Redirect After Post technique.
But if you already are using it and can't change, you can bypass the POST check from within your Interceptor by checking if the result is of type chain:
public String intercept(ActionInvocation ai) throws Exception {
final ActionContext context = ai.getInvocationContext();
HttpServletRequest request = (HttpServletRequest)
context.get(StrutsStatics.HTTP_REQUEST);
boolean isChainResult = ai.getResult() != null
&& ActionChainResult.class.isAssignableFrom(ai.getResult().getClass());
if (!request.getMethod().equals("POST") && !isChainResult) {
return Action.ERROR;
}
return ai.invoke();
}
My goal is that all below URI's should work
https://rest/xyz?sort=name
https://rest/xyz?Sort=name
https://rest/xyz?filter=name=value
https://rest/xyz?Filter=name=value
To achieve this, I have created custom filter that overrides the HttpServletRequest that is passed to the FilterChain. Below is the link for this approach:
http://forum.springsource.org/archive/index.php/t-87433.html
My code:
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class HttpCustomParamFilter implements Filter
{
private static class HttpServletRequestCustomeWrapper extends HttpServletRequestWrapper
{
private String[] parameterValues;
#Override
public String[] getParameterValues(String name)
{
Map<String, String[]> localParameterMap = super.getParameterMap();
// Handle case insensitivity of http request paramters like start, count, query, sort, filter etc.
if (localParameterMap != null && !localParameterMap.isEmpty())
{
parameterValues = new String[localParameterMap.size()];
for (String key : localParameterMap.keySet())
{
if (name.equalsIgnoreCase(key))
parameterValues = localParameterMap.get(key);
else
parameterValues = null;
}
}
return parameterValues;
}
public HttpServletRequestCustomWrapper(final ServletRequest request)
{
super((HttpServletRequest) request);
}
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// override the request passed to the FilterChain
chain.doFilter(new HttpServletRequestCustomWrapper(request), response);
}
#Override
public void init(FilterConfig filterConfig)
throws ServletException
{
// TODO Auto-generated method stub
}
#Override
public void destroy()
{
// TODO Auto-generated method stub
}
}
In this code, i have overriden getParameterValues(String name) method and achieved case-insensitivity of request paramters, but not sure if i need to override any other methods.
my doubts:
do i need to override other methods also like getParameter() and getParameterNames()?
what internal implementation is impacted with this?
which class i can see the code implementation of getParameter(), getParameterNames() and getParameterValues()?
First, let me say my peace: I don't think modifying the HttpServletRequestWrapper is the way to go. I am not even sure how you would go about using it, as my understanding is it's App Server specific. As a side note, this article has specifics on how to use the HttpServletRequest to get a case-insensitive query param without rolling your own.
But, in the spirit of answering your questions:
Do you need to override getParameter() and getParameterNames()? You could, as it would give you the ability to manipulate the case. In fact, I would say the safest way to make the query parameters case-insensitive would be to overwrite ONLY those methods. Make the getParameter() call do a case-insensitive equals on the string names. Not sure what you would do with getParameterNames(), maybe return every possible case, but this seems redundant.
What internal implementation is impacted by this? I am not certain. HttpServletRequest is so core to pretty much everything, there is no telling what you could introduce if your code is not 100% solid. For instance, Spring has a SecurityContextHolderAwareRequestWrapper, so does that mean you just broke Spring Security? No telling without a lot of testing.
Which class can I see the code implementation of getParameter(), getParameterNames(), and getParameterValues()? HttpServletRequestWrapper is the only implementation of HttpServletRequest interface, according to the JavaDocs. The actual implementation of this class is dependent on your application container. For instance, in my app its weblogic.servlet.internal.ServletRequestImpl, since I use Web Logic. Hopefully you are using an open-source app server that has the code readily available. The way I found this was to put a break in one of my Controller handler methods that has HttpServletRequest defined and viewing it's getClass() response in the debugger.