I'm trying to write my own Swagger API Filter to hide certain things from appearing in the API. I think I have a pretty good idea of what I should be doing but I must be missing something in the details.
I'm using Swagger Core 1.3.10 and Spring 3.1. Here's some of what I have now:
SwaggerApiAuthorizationFilter
public class SwaggerApiAuthorizationFilter implements SwaggerSpecFilter {
private final static Logger logger = LoggerFactory.getLogger(SwaggerApiAuthorizationFilter.class);
#Override
public boolean isOperationAllowed(Operation operation, ApiDescription api, Map<String, List<String>> params,
Map<String, String> cookies, Map<String, List<String>> headers) {
return true;
}
#Override
public boolean isParamAllowed(Parameter parameter, Operation operation, ApiDescription api, Map<String, List<String>> params,
Map<String, String> cookies, Map<String, List<String>> headers) {
// do not allow the documentation to be generated on parameters that have their access set to "internal"
if (parameter.paramAccess().isDefined() && parameter.paramAccess().get().equalsIgnoreCase("internal")) {
logger.debug("The following parameter has been hidden from the Swagger API documentation: " + parameter.name());
return false;
} else {
return true;
}
}
}
SwaggerServlet
public class SwaggerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final static Logger logger = LoggerFactory.getLogger(SwaggerServlet.class);
BeanConfig beanConfig;
SwaggerApiAuthorizationFilter swaggerApiAuthorizationFilter;
public void setBeanConfig(BeanConfig beanConfig) {
this.beanConfig = beanConfig;
}
public void setSwaggerApiAuthorizationFilter(SwaggerApiAuthorizationFilter swaggerApiAuthorizationFilter) {
this.swaggerApiAuthorizationFilter = swaggerApiAuthorizationFilter;
}
#Override
public void init(ServletConfig servletConfig) throws ServletException {
try {
super.init(servletConfig);
beanConfig.setBasePath("/mbl/services");
beanConfig.setVersion("1.0");
beanConfig.setResourcePackage("com.whatever.resources");
beanConfig.setScan(true);
FilterFactory.setFilter(swaggerApiAuthorizationFilter);
logger.debug("The Swagger servlet has been initialized");
} catch (Exception e) {
e.printStackTrace();
}
}
}
spring config file
<!-- Swagger Configuration and Providers -->
<bean id="beanConfig" class="com.wordnik.swagger.jaxrs.config.BeanConfig">
<property name="title" value="Java API"/>
<property name="version" value="1.0" />
<property name="basePath" value="/mbl/services"/>
<property name="resourcePackage" value="com.whatever.resources"/>
<property name="scan" value="true"/>
</bean>
<bean id="swaggerApiAuthorizationFilter" class="com.whatever.util.SwaggerApiAuthorizationFilter" />
web.xml
<!-- Enabling Swagger servlet -->
<servlet>
<servlet-name>Swagger Servlet</servlet-name>
<servlet-class>com.whatever.web.SwaggerServlet</servlet-class>
<init-param>
<param-name>swagger.filter</param-name>
<param-value>com.whatever.util.SwaggerApiAuthorizationFilter</param-value>
</init-param>
<load-on-startup>-1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Swagger Servlet</servlet-name>
<url-pattern>/api-docs</url-pattern>
</servlet-mapping>
Resource class
public Response getUserInfo(
#Context HttpHeaders headers,
#ApiParam(name="brand", access="internal") #CookieParam(value = "brand") String brand) {
It appears that my SwaggerApiAuthorizationFilter isn't even being called. My guess is that something is wrong in my configuration either in the web.xml or the SwaggerServlet. Does anyone see anything above that looks incorrect or does anyone have any ideas?
I've just used the filter successfully. I had problems to make filter function properly. The problem was that there was a problem to find my filter's implemention class. When I changed the package to a place where swagger's class could access, everything funtioned correctly. Next, I just had to implement my logic on my filter class. My filter class implemented the interface SwaggerSpecFilter too. Best regards.
I'm using DefaultJaxrsConfig:
<servlet>
<servlet-name>Jersey2Config</servlet-name>
<servlet-class>io.swagger.jaxrs.config.DefaultJaxrsConfig</servlet-class>
<init-param>
<param-name>api.version</param-name>
<param-value>1.0.0</param-value>
</init-param>
<init-param>
<param-name>swagger.api.basepath</param-name>
<param-value>/my-service/service/</param-value>
</init-param>
<init-param>
<param-name>swagger.filter</param-name>
<param-value>mypackage.SwaggerFilter</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
I'm just guessing but I think you need to implement Filter as well as the SwaggerSpecFilter interface instead of extending HttpServlet (looking at the source it does not appear SwaggerSpecFilter extends Filter as one would expect), then configure it as a filter in your web.xml instead of a servlet. It would be something like this:
<filter>
<filter-name>Swagger Filter</filter-name>
<filter-class>com.whatever.util.SwaggerApiAuthorizationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Swagger Filter</filter-name>
<url-pattern>/api-docs</url-pattern>
</filter-mapping>
Again, not sure if this will work but just a guess off the top of my head.
Related
I have a simple Jersey POST method that accepts JSON. I am using SOAP UI to test, using POST and media type application/JSON.
Within the request body I have JSON:
{
email:"test"
}
When I test this, the string I expect to come into the service as test actually comes in as the entire JSON string. Not sure what's wrong here, it should work according to the docs.
#POST
#Path("/TEST")
#Consumes(MediaType.APPLICATION_JSON)
public Response testJaxRs(String email){
// email = "{
// email:"test"
// }"
return Response.ok().build();
}
If you do not want to deserialize it to a bean, then you can accept the post body as a map of key-value pairs. For example:
public Response testJaxRs(Map<String, String> body) {
body.get("email") // "test"
...
You need a bean
public class Email{
private String email;
//getters&setters
}
public Response testJaxRs(Email emailBean){
Note that you need to add the dependencies to decode json. e.g Jackson and add this mapping to jersey servlet in web.xml> See full example here: https://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/
<web-app ...>
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.yourpackage</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
If you're using Jersey 1.x, then to receive your Json as an object you need to declare the POJO and annotate it with javax.xml.bind.annotation.XmlRootElement:
#XmlRootElement
public class EmailTest{
String email;
public String getEmail(){
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
}
Then you have to use this class in your controller:
#POST
#Path("/TEST")
#Consumes(MediaType.APPLICATION_JSON)
public Response testJaxRs(Email email){
System.out.println(email.getEmail()); // prints "test"
return Response.ok().build();
}
Finally you've to add com.sun.jersey.config.property.packages as <init-param> of your servlet adding the package or packages (separated by ,) pointing to your controller packages. For example in your web.xml:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.app.controllers</param-value>
</init-param>
using latest Jersey (2.22.1), I've successfully created my custom validators for various needs. But my custom ExceptionMapper (registered as a provider in web.xml) is not invoked when a ConstraintViolationException occurs, altough it is defined as an ExceptionMapper<Throwable>.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="mywebapp" version="2.5">
<display-name>Some Name - Webapp</display-name>
[...]
<servlet>
<servlet-name>jersey_v2-servlet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.myfirm.web.rest.providers.DefaultExceptionMapper,
com.myfirm.web.rest.endpoints.XxxEndpoint,
com.myfirm.web.rest.endpoints.XxyEndpoint,
com.myfirm.web.rest.endpoints.XyzEndpoint
</param-value>
</init-param>
<init-param>
<param-name>jersey.config.beanValidation.enableOutputValidationErrorEntity.server</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey_v2-servlet</servlet-name>
<url-pattern>/rest/1.0/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>jersey_v2-servlet</servlet-name>
<url-pattern>/rest/latest/*</url-pattern>
</servlet-mapping>
[...]
</web-app>
DefaultExceptionMapper
#Provider
public class DefaultExceptionMapper implements ExceptionMapper<Throwable> {
private static final Logger LOG = LoggerFactory.getLogger(DefaultExceptionMapper.class);
#Override
public Response toResponse(Throwable caughtException) {
LOG.warn("Exception caught in the REST layer", caughtException);
Throwable original = caughtException;
// some business logic to convert the exception to a response
// log & return the response
Response response = status(status).entity(entity).build();
return response;
}
#XmlRootElement
public static class Error {
#XmlElement
public String type;
#XmlElement
public String message;
#XmlElement
public String translationKey;
}
}
Using my debugger, I can see in the class org.glassfish.jersey.server.ServerRuntime, line 596, that the resolved mapper is not mine, but is a org.glassfish.jersey.server.validation.internal.ValidationExceptionMapper.
How can I tell Jersey to use my DefaultExceptionMapper in the case of a ConstraintViolationException ?
PS: I've tried options suggested here: ExceptionMapper not invoked if receiving invalid JSon with no luck.
Defining the mapper as a implements ExceptionMapper<ConstraintViolationException> made it somehow take precedence over other mapper registered for the same exception type.
I've ended up with 2 exception mappers, one for every exceptions, the other for ConstraintViolationException, both extending the same abstract class.
There's an easy workaround these days, by completely disabling Jersey's bean validation. This can be done by having your Application sub class return a property that sets ServerProperties.BV_FEATURE_DISABLE to true.
For instance:
#ApplicationPath("")
public class MyApplication extends Application {
#Override
public Map<String, Object> getProperties() {
return Collections.singletonMap(ServerProperties.BV_FEATURE_DISABLE, true);
}
}
I'm working on creating a security library that will be used by several RESTful clients. I'm using Java EE 5, Jersey 1.17 and Maven. The clients will use my library to call a third party app using a token. The third party app will then return all the information it has on that token, like expiration, scope and userId.
My idea is to make a filter that will check if there is an Authorization header, and if that's so, it calls the third party app. If the third party app validates the token and returns the token's info, I need to return that information, stored in a TokenInformation object, back to the resources. In a previous post, someone said that I could do this:
public class MyFilter implements Filter{
#Override
public void doFilter(final ServletRequest request,
final ServletResponse response,
final FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String header = req.getHeader("Authorization");
TokenInformation info = new TokenInformation();
info = buildInfo(info);
if (true) {
request.setAttribute("auth", info);
chain.doFilter(request, response);
} else {
handleError(response);
}
}
}
So, by sending the TokenInformation object to the request as an additional attribute, I would be able to retrieve it later in the resource classes. The thing is that I'm using Java EE 5, and I didn't realize that I couldn't use the #Context annotation to inject the ServletRequest object. How can I access the ServletRequest object again from a resource class, so that I can access the TokenInformation object in, for example, the DAO?
The way I'm using jersey is by doing this in my web.xml:
<servlet>
<servlet-name>Security API</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.ni.apps.engineering.securitylibrary.resources.SecurityResource</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Security API</servlet-name>
<url-pattern>/1/*</url-pattern>
</servlet-mapping>
The SecurityResource class has this:
public class SecurityResource extends Application{
public static final String SUPPORTED_REPRESENTATIONS = MediaType.APPLICATION_XML
+ "," + MediaType.APPLICATION_JSON;
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>();
set.add(Security.class);
return set;
}
}
The Security class has this:
#Path("")
public class Security implements ISecurity{
#Override
public Response get(String upId) {
String test = "";
try{
TokenInformation tI = (TokenInformation) HttpServletRequestWrapper.
test = "test1";
}catch(Exception e){
System.out.println(e.getMessage());
}
return null;
}
}
You don't have to access ServletRequest at Dao layer.
In Servlet you can get ServletRequest object and you can pass the value to Dao layer.
If you really want to access then pass ServletRequest object to Dao layer by reference.
<servlet-name>Security API</servlet-name>
<servlet-class>com.packagename.MyServlet</servlet-class>
public MyServlet extends com.sun.jersey.spi.container.servlet.ServletContainer{}
You can extend jersey servlet and you can initialize Servlet with Application Class by programatically.
In MyServlet you can reach the request object.
Servlet Information :
https://jersey.java.net/apidocs/1.17/jersey/com/sun/jersey/spi/container/servlet/ServletContainer.html
I am using Java with Jersey 1.16.
Is the name of a #FormParam parameter case sensitive?
Can I somehow force it to be NON case sensitive? (hopefully in some global way).
You could filter the requests:
public class CaseFilter implements ContainerRequestFilter {
#Override
public ContainerRequest filter(ContainerRequest request) {
for (Entry<String, List<String>> entry: request.getFormParameters().entrySet()) {
request.getFormParameters().put(entry.getKey().toLowerCase(), entry.getValue());
}
return request;
}
}
And update web.xml accordingly:
...
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>org.example.CaseFilter</param-value>
</init-param>
...
Can we write an argument constructor in a Servlet? If yes, how can you call?
Can we write an argument constructor in a Servlet?
Yes, you can but it is useless since the servlet container won't invoke it.
The proper way to do it is to use the init() method:
#Override
public void init() throws ServletException {
String foo = getInitParameter("foo");
String bar = getServletContext().getInitParameter("bar");
// ...
}
In this example, getInitParameter("foo") returns the value of the <init-param> of the specific <servlet> entry in web.xml, and getServletContext().getInitParameter("bar") returns the value of the independent <context-param> in web.xml.
The problem can be state more generically:
"According to the servlets (2.3)
specification, the servlets are
instantiated by the servlet engine by
invoking the no-arg constructor. How
can I initialize a servlet properly
given that correct initialization
depends on the
central/global/unique/application
configuration?"
Actually, you can use serlvets with constructor and/or initialize them as you like. However, it requires a little bit of plumbing.
Assuming you have a servlet with a constructor having arguments:
package org.gawi.example.servlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class SampleServlet extends HttpServlet
{
private final String mMessage;
public SampleServlet(String message)
{
mMessage = message;
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setContentType("text/plain");
response.getWriter().write(mMessage);
}
}
The first thing you need is a unique servlet whithin your application, let's call it InitializationServlet, to create all the required instances. Those instances must then be exported in the servlet context to be retrieve by another servlet (explained later). The InitializationServlet may look like this:
package org.gawi.example.servlets;
import javax.servlet.*;
import javax.servlet.http.*;
public class InitializationServlet extends HttpServlet
{
public void init() throws ServletException
{
SampleServlet servletA = new SampleServlet("this is servlet A");
SampleServlet servletB = new SampleServlet("this is servlet B");
SampleServlet servletC = new SampleServlet("this is servlet C");
getServletContext().setAttribute("servletA", servletA);
getServletContext().setAttribute("servletB", servletB);
getServletContext().setAttribute("servletC", servletC);
}
}
You see that only the init() method has been provided. This servlet is not servicing any HTTP request. Its only purpose is to store the servlet in the ServletContext. Note that you could have also use this servlet to load your application configuration. So this can act as the web-application entry point, like the main(String[] args) method of a program. This might remind you of the ContextLoaderServlet of SpringSource.
The last piece is the DelegateServlet that will effectively be instantiated by the servlet container, only this servlet will forward all the pertinent method calls to the wrapped servlet instance:
package org.gawi.example.servlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class DelegateHttpServlet extends HttpServlet
{
private static final String SERVLET_CONTEXT_KEY_INIT_PARAMETER = "servletContextKey";
private HttpServlet mServlet;
public void init(ServletConfig servletConfig) throws ServletException
{
super.init(servletConfig);
locateServlet(servletConfig);
mServlet.init(servletConfig);
}
private void locateServlet(ServletConfig servletConfig) throws ServletException
{
String servletContextAttributeName = servletConfig.getInitParameter(SERVLET_CONTEXT_KEY_INIT_PARAMETER);
if (servletContextAttributeName == null)
{
throw new ServletException("Unable to find init parameter '" + SERVLET_CONTEXT_KEY_INIT_PARAMETER + "'");
}
Object object = servletConfig.getServletContext().getAttribute(servletContextAttributeName);
if (object == null)
{
throw new ServletException("Unable to find " + servletContextAttributeName + " in servlet context.");
}
if (!(object instanceof HttpServlet))
{
throw new ServletException("Object is not an instance of"
+ HttpServlet.class.getName()
+ ". Class is "
+ object.getClass().getName()
+ ".");
}
mServlet = (HttpServlet) object;
}
public void destroy()
{
mServlet.destroy();
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
mServlet.service(req, res);
}
}
During its initialization, the DelegateServlet will look-up the target servlet in the servlet context using the servletContextKey servlet initial argument.
The web.xml for such an application might look like that:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Example</display-name>
<description>Example web showing handling of servlets w/ constructors.</description>
<servlet>
<servlet-name>Initialization</servlet-name>
<servlet-class>org.gawi.example.servlets.InitializationServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>A</servlet-name>
<servlet-class>org.gawi.example.servlets.DelegateHttpServlet</servlet-class>
<init-param>
<param-name>servletContextKey</param-name>
<param-value>servletA</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<servlet-name>B</servlet-name>
<servlet-class>org.gawi.example.servlets.DelegateHttpServlet</servlet-class>
<init-param>
<param-name>servletContextKey</param-name>
<param-value>servletB</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet>
<servlet-name>C</servlet-name>
<servlet-class>org.gawi.example.servlets.DelegateHttpServlet</servlet-class>
<init-param>
<param-name>servletContextKey</param-name>
<param-value>servletC</param-value>
</init-param>
<load-on-startup>4</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>A</servlet-name>
<url-pattern>/servlet/a</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>B</servlet-name>
<url-pattern>/servlet/b</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>C</servlet-name>
<url-pattern>/servlet/c</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>5</session-timeout>
</session-config>
</web-app>
Be sure to load the InitializationServlet first, using a low <load-on-startup> value.
The benefit of this approach is that HttpServlet objects can be handled like any other regular Java object or bean. Hence, it provides a better control over initialization: no more tricky stuff to do in the init() method, nor messy servlet init-arg handling.
You can't. Servlet is instantiated reflectively by container. If servlet spec have allowed arguments in constructor, you would have to have some complicated deployment descriptor like,
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>MyServlet</servlet-class>
<servlet-argument id="1" type="string">Test<servlet-argument>
<servlet-argument id="2" type="int">10</servlet-argument>
<load-on-startup>1</load-on-startup>
</servlet>
I guess no one wants that.
Constructors are objects managed by the application server.
For initialization, see the init() method.
Update:
Can I use a constructor in my servlet?
A: A servlet is a normal Java class, so when there are no custom
constructors, there is an implicit
default constructor with no arguments.
Servlet containers typically use the
Class.newInstance() method to load
servlets, so you must be careful to
add an explicit default constructor if
you add non-default constructors.
source: http://www.codestyle.org/java/servlets/FAQ.shtml