小编ras*_*orp的帖子

不直接编写Servlet以创建REST API的原因

在我目前的公司中,我们正在开始一个新项目,该项目将是Java中的REST API,部署在像Tomcat这样的servlet容器中.在我之前使用REST框架(如JAX-RS和Jersey,JBOSS REST Easy,Spring MVC)的经验中,我知道使用类似于直接编写Servlet来处理请求的框架的一些优点.

(当然我们知道所提到的框架仍然使用Servlets)

我发现很难说服他们.因为他们建议编写servlet,认为它对性能更好(可能是这种情况,但我认为使用其中一个框架的开销对于REST API来说应该是微不足道的).

这是我的理由:

1)更少的样板和更简洁的代码(更易于维护和测试).使用JAX-RS框架或SpringMVC,您可以通过编写带有注释的方法来非常轻松地定义REST资源,注释指示资源的PATH,要使用的http方法,查询和url参数,接受的编码等标题等.

例:

@GET
@Path("/users")
@Produces({MediaType.APPLICATION_JSON}) 
public UserList getUsers(@QueryParam("group") String group) {
    return userService.findUsers(group);
}
Run Code Online (Sandbox Code Playgroud)

使用servlet,您至少需要这样的东西:

在web.xml中映射每个servlet的url(在Servlet 3.0中不需要):

<servlet>
    <servlet-name>UsersServlet</servlet-name>
    <servlet-class>test.UsersServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>UsersServlet</servlet-name>
    <url-pattern>/users</url-pattern>
</servlet-mapping>
Run Code Online (Sandbox Code Playgroud)

然后在servlet类中:

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
    String group = request.getParameter("group");
    response.setContentType("application/json");
    PrintWriter out = response.getWriter();
    JsonSerializer someJsonSerializer = new JsonSerializer();
    String json = someJsonSerializer.serialize(userService.findUsers(group));      
    out.print(json);
}
Run Code Online (Sandbox Code Playgroud)

2)适应性.上述框架允许您轻松地向应用程序添加功能,否则您将需要手动执行,例如使用多个媒体类型输入和输出.例如,根据accept标头,使服务返回xml或json或任何其他服务.像SpringMVC和Jersey这样的框架可以很容易地为您的请求和响应配置序列化器/反序列化器.

3)REST最佳实践.通常,这些框架是基于对REST API遵循的最佳实践的充分理解而构建的,并且基于REST体系结构的标准进行定义,这使得构建可靠且标准的符合标准的应用程序变得更加容易.另一方面,Servlets为您提供了如何处理请求/响应的高度自由,以至于您很难意识到您根本不是RESTfull.

任何其他?

java rest servlets jax-rs spring-mvc

22
推荐指数
3
解决办法
2万
查看次数

当杰克森反序列化失败时,Jersey异常映射器无法正常工作

我在我的REST API中使用Jersey 2.10和Jackson序列化/反序列化功能.

我的想法是让我的REST API始终返回标准的JSON错误响应.为此,我有ExceptionMapper类,为Jersey应用程序中抛出的任何异常构建正确的json错误响应.我还有一个生成相同类型的JSON响应的jsp,我在web.xml中注册为error-page,它涵盖了在加载Jersey之前可能出现的所有错误.

但是有一种情况,我的Exception mappers和我的json生成jsp都没有工作,就是当一个坏的形成的json发送到POST REST端点时,它只返回以下消息:

HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, PUT
Content-Type: text/plain
Content-Length: 210
Date: Tue, 24 Jun 2014 22:14:11 GMT
Connection: close

Can not deserialize instance of com.example.rest.User[] out of START_OBJECT token
 at [Source: org.glassfish.jersey.message.internal.EntityInputStream@1dcccac; line: 1, column: 1]
Run Code Online (Sandbox Code Playgroud)

如何让泽西岛返回我的自定义错误响应而不是这个?

更新:

基于@Lucasz的答案,我做了更多的研究,发现在com.fasterxml.jackson.jaxrs.base包中定义了两个异常映射器(https://github.com/FasterXML/jackson-jaxrs-providers/ tree/master/base/src/main/java/com/fasterxml/jackson/jaxrs/base)JsonMappingExceptionMapper和JsonParseExceptionMapper似乎正在影响我的自定义映射器.

如何取消注册那些映射器?

这就是我目前正在注册地图制作者的方式:

@ApplicationPath("/")
public class MyApp extends ResourceConfig{
    public SyntheticAPIApp() {
        packages("com.example.resource", "com.example.mapper");
        register(org.glassfish.jersey.jackson.JacksonFeature.class);
    }
}
Run Code Online (Sandbox Code Playgroud)

java rest jersey jackson

15
推荐指数
1
解决办法
1万
查看次数

如何使用Maven在Vaadin 7中仅编译必要的小部件?

我是Vaadin框架的新手,我看起来非常有趣,使用eclipse和maven来开发和构建我的应用程序我觉得很烦人每次我做一个mvn干净安装它需要很长时间来构建应用程序,我发现这是因为它编译了整套小部件.

即使我只在我的布局中使用Button,也会在构建应用程序时花费太多.

我已经研究了一段时间在互联网和2本书,但找不到足够的信息,如何使它只编译我正在使用的组件,而不是整套.

我使用maven原型创建了项目:

mvn archetype:generate -DarchetypeGroupId=com.vaadin -DarchetypeArtifactId=vaadin-archetype-application -DarchetypeVersion=7.1.9
Run Code Online (Sandbox Code Playgroud)

我确信每次构建战争时都会编译widgetset,当我执行mvn clean时会删除目录:/ src/main/webapp/VAADIN/widgetsets和/ src/main/webapp/VAADIN/gwt-unitCache

当我运行mvn install时,构建将持续超过3分钟:

...
[INFO]    Compiling 6 permutations
[INFO]       Compiling permutation 0...
[INFO]       Process output
[INFO]          Compiling
[INFO]             Compiling permutation 1...
[INFO]       Process output
[INFO]          Compiling
[INFO]             Compiling permutation 3...
[INFO]       Process output
[INFO]          Compiling
[INFO]             Compiling permutation 2...
[INFO]       Compiling permutation 4...
[INFO]          Compiling
[INFO]             Compiling permutation 5...
[INFO]    Compile of permutations succeeded
[INFO] Linking into /.../example/src/main/webapp/VAADIN/widgetsets/com.my.example.AppWidgetSet; Writing extras to /.../example/target/extra/com.my.example.AppWidgetSet
[INFO]    Link succeeded
[INFO]    Compilation succeeded …
Run Code Online (Sandbox Code Playgroud)

java eclipse vaadin maven vaadin7

14
推荐指数
1
解决办法
1万
查看次数

是否有针对varargs阵列的Mockito eq匹配器?

尝试将作为参数传递的数组匹配到接收varargs数组的方法时,我遇到了问题.

其他问题/答案中提到的anyVararg()匹配器对我不起作用,因为我想确保提供的数组是我需要的数组.

我把问题简化为这个更易于理解和抽象问题的例子(我的真正问题是生产代码并具有业务逻辑,因此对于这个问题的目的而言会令人困惑):

@RunWith(MockitoJUnitRunner.class)
public class UnitTest {
    private Object[] objectArray;
    private List<Object> expected;
    private TestTarget target;

    @Before
    public void setUp() {
        objectArray = new Object[]{ new Object() };
        expected = Arrays.asList(new Object(), new Object());
        target = Mockito.spy(new TestTarget());
    }

    @Test
    public void testMakeList() { // this pass as eq works well with normal array
        doReturn(expected).when(target).toList(Mockito.eq(objectArray));
        Assert.assertEquals(expected, target.makeList(objectArray));
    }

    @Test
    public void testMakeList1() { // this one fails as eq is not working with varargs
        doReturn(expected).when(target).toList1(Mockito.eq(objectArray));
        Assert.assertEquals(expected, target.makeList1(objectArray));
    }

    @Test …
Run Code Online (Sandbox Code Playgroud)

java junit mockito

7
推荐指数
1
解决办法
7656
查看次数

Swagger-core Scala依赖项过大

我发现在我开始为我的REST API使用swagger文档工具之后,我的war文件的大小增加了近4.5倍,从8.7 MB增加到39MB.我正在使用Maven来构建项目.

这是因为Swagger Scala依赖大尺寸,特别是scala编译器.所以我试图找出哪些依赖项并不是真正需要的.我在项目的Github页面中创建了一个新问题:https://github.com/wordnik/swagger-core/issues/624他们回答说删除任何scala依赖项不是一个好主意,因为框架是用该语言编写的这可能会破坏它.还建议将依赖项放在容器/服务器而不是战争中.

java maven swagger

7
推荐指数
1
解决办法
1110
查看次数

相当于@JsonIgnore,但仅适用于使用Jackson的xml字段/属性转换

我有一个类,我使用Jackson对JSON,XML进行序列化/反序列化.

public class User {
    Integer userId;
    String name;
    Integer groupId;
...
}
Run Code Online (Sandbox Code Playgroud)

我想在进行xml处理时忽略groupId,所以我的XML不会包含它:

<User>
 <userId>...</userId>
 <name>...</name>
</User>
Run Code Online (Sandbox Code Playgroud)

但是JSON会:

{
  "userId":"...",
  "name":"...",
  "groupId":"..."
}
Run Code Online (Sandbox Code Playgroud)

我知道@JsonIgnore可以兼用,但我只想在xml中忽略它.

我知道可用于执行此操作的混合注释(/sf/answers/1603477641/),但我认为应该有一个简单的注释来执行此操作,但无法找到它.杰克逊的文档(至少对我而言)并不像我想要找到这些东西时那样好.

java xml json jackson

6
推荐指数
1
解决办法
5696
查看次数

如何使 Spring Security OAuth2 与负载均衡器一起使用?

我们目前有 4 个使用 Spring Security Oauth2 项目进行身份验证的 Spring 应用程序。这些应用程序是 REST API,供我工作的公司中的其他内部应用程序使用。

在开发和 QA 环境中一切正常,因为我们没有进行负载平衡,现在我们处于预生产阶段,我们正面临负载平衡器 (LB) 的问题。

这是此问题的工作流程:

  1. 客户端发送对 oauth 令牌的请求
  2. LB 将请求重定向到 Box 1
  3. 框 1 进行身份验证并返回有效的承载令牌
  4. 客户端接收令牌并将其存储以通过会话使用
  5. 客户端在 REST API 中发送对服务的请求,将先前检索到的令牌添加到标头中
  6. LB 将请求重定向到 Box 2
  7. Box 2 无法进行身份验证,因为它无法识别令牌并返回 Invalid Credentials 响应

我们正在使用内存用户存储:

<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />
Run Code Online (Sandbox Code Playgroud)

有没有办法让不同的盒子共享同一个令牌存储?我知道有一个 JdbcTokenStore 可用于将令牌持久化到数据库,但我更愿意避免持久化令牌,因为这些应用程序指向仅存储业务信息的旧数据库。

java spring spring-security oauth-2.0

5
推荐指数
2
解决办法
5603
查看次数

如何在Spring Security 3.1中处理不同的身份验证异常?

首先,我想评论一下,我已经检查了Stack Overflow中的其他问题,并根据答案实现了我自己的方法:https://stackoverflow.com/a/14425801/2487263/sf/answers/1127115461/

我试图在Spring 3.2,Spring MVC应用程序中使用Spring安全3.1来保护REST API,我使用简单配置的基本身份验证方法:

<http create-session="stateless" entry-point-ref="authenticationFailedEntryPoint">
    <intercept-url pattern="/**" access="ROLE_USER"/>
    <http-basic />
</http>
Run Code Online (Sandbox Code Playgroud)

如您所见,我使用自定义入口点,我有自己的ErrorResponse对象,我将以json格式添加到http响应,请参阅下面的代码:

    @Component
public class AuthenticationFailedEntryPoint implements AuthenticationEntryPoint {
    static Logger log = Logger.getLogger(AuthenticationFailedEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        log.error(ExceptionUtils.getStackTrace(authException));
        ErrorResponse errorResponse = new ErrorResponse();  

            ... here I fill my errorResponse object ...

        ObjectMapper jsonMapper = new ObjectMapper();

        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(status); 

        PrintWriter out = response.getWriter();
        out.print(jsonMapper.writeValueAsString(errorResponse));   
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试了两种测试用例的方法:

  1. 尝试使用一个服务而不提供基本身份验证标头:

这是请求/响应:

GET http://localhost:8081/accounts/accounts?accountNumber=1013


 -- …
Run Code Online (Sandbox Code Playgroud)

java spring spring-mvc spring-security basic-authentication

5
推荐指数
1
解决办法
1万
查看次数

使用不带web.xml的ResourceConfig的Jersey应用程序的URL

我使用带有Jersey 2.7的ResourceConfig并在Tomcat 7上进行部署,从web.xml迁移到完全Java配置.之后,我无法使用与web.xml方法相同的URL来访问服务.我不明白ResourceConfig如何影响路径.

我以前的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_3_0.xsd"    
version="3.0">
<servlet>
    <servlet-name>my.app</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>com.mypackage.resource,com.mypackage.providers</param-value>
    </init-param>
    <init-param>
        <param-name>jersey.config.server.provider.scanning.recursive</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>org.glassfish.jersey.filter.LoggingFilter</param-value>        
    </init-param>
    <init-param>
        <param-name>org.glassfish.jersey.server.ServerProperties.BV_SEND_ERROR_IN_RESPONSE</param-name>
        <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>my.app</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
Run Code Online (Sandbox Code Playgroud)

扩展ResourceConfig的配置类是:

MyRESTAPIApp.java

@ApplicationPath("")
public class MyRESTAPIApp extends ResourceConfig{
    public MyRESTAPIApp () {
        packages("com.mypackage.resource", "com.mypackage.providers");
        register(org.glassfish.jersey.filter.LoggingFilter.class);
        property("jersey.config.beanValidation.enableOutputValidationErrorEntity.server", "true");
    }
}
Run Code Online (Sandbox Code Playgroud)

我的一个资源是:

FlagResource.java

@Path("my-resource")
public class FlagResource {
private MyService myService = new MyService();

@GET
@Produces(MediaType.APPLICATION_JSON)
public FlagResource getFlagResource(@NotNull @QueryParam("level") Long level) { …
Run Code Online (Sandbox Code Playgroud)

java jersey tomcat7

5
推荐指数
1
解决办法
1万
查看次数

是否可以在Spring MVC REST端点中验证@RequestParam?

在泽西岛2,可以这样做:

@GET
@PATH("user/{email}")
public IDto getUser(@NotNull @Email @PathParam("email") String validEmail) {
    return userManagementService.findUserByEmail(validEmail);
}
Run Code Online (Sandbox Code Playgroud)

但我无法在Spring MVC中创建类似的东西,似乎验证只在@RequestBody中提供对象或使用SpringMVC表单时完成,例如以下操作无效:

@RequestMapping(value="/user/{email}", method = RequestMethod.GET)
public @ResponseBody IDto getUser(@NotNull @Email @PathVariable String validEmail) {
    return userManagementService.findUserByEmail(validEmail);
}   
Run Code Online (Sandbox Code Playgroud)

还有其他类似的问题,但那些似乎是面向Spring MVC UI应用程序,在我的情况下,它只是一个返回JSON响应的REST API,因此我没有任何View映射/绑定到控制器.

java spring spring-mvc

5
推荐指数
2
解决办法
5447
查看次数