是的,我知道,最好的网络框架的老问题......但让我解释一下.
我正在寻找基于Java Servlet的Web框架,它允许RESTful交互,也适合构建Web GUI.
我想要的是:
候选人:
Spring MVC功能强大,但我厌倦了Spring配置,不喜欢编程模型.我认为它有点过于抽象和灵活,因此需要进行大量配置.我不喜欢Spring MVC使用注释的方式.但是也存在一些设计缺陷,比如return通过输出参数返回值的方法- 真的很难看!我认为使用Spring MVC和Java EE依赖注入并不容易,因为Spring MVC严重依赖于Spring DI.
Roo似乎很酷,但它只是创建一个Spring MVC应用程序的另一种方式,它用AOP做了一些奇怪的事情.
Struts有点尴尬和outdatet.
Stripes ActionBean方法看起来并不比Struts好多少.我不喜欢它.
另请参阅10个最佳Java Web框架
我不是在服务器上寻找具有UI状态的框架,如Wicket,Tapestry或JSF.我认为这种方法与网络的基本原则相矛盾!
那么该怎么办?从头开始编写框架?嗯......
我想拥有像JAX-RS这样的东西,支持经典的浏览器GUI.例如,框架应支持验证并将验证错误放入重新显示的表单中.有类似的东西吗?有什么建议?
我读到在请求中传递数组的HTTP方法是多次设置一个参数:
1) GET /users?orderBy=last_name&orderBy=first_name
Run Code Online (Sandbox Code Playgroud)
但是,我也看到了逗号分隔的参数(我觉得这是"更干净"):
2) GET /users?orderBy=last_name,first_name
Run Code Online (Sandbox Code Playgroud)
我想实现多排序(通过last_name排序用户,然后复制last_names由first_name排序).代码方面,这很容易(谷歌的番石榴图书馆拯救),但我应该如何揭露这个?第一种方法是否保留字段的顺序(按last_name排序,然后按first_name排序)?
如果在请求中多次设置,Spring会将参数神奇地转换为String []数组:
... @RequestParam("orderBy") String[] orderBy ... becomes ["last_name","first_name"]
Run Code Online (Sandbox Code Playgroud)
这让我相信第一种方式被认为是最佳实践,尽管我喜欢第二种方式......
我有一个相当典型的REST API,除了资源的id不是整数,而是字符串,通常包含/字符.因此,如果客户的ID是string/with/slashes该客户的URI应该是http://localhost/customers/string%2Fwith%2Fslashes.返回客户列表时,我想用UriBuilder构造URI,这样我就可以把它放在ATOM风格的链接元素的href中.但它不太有效; 这是一个小测试类,显示我的意思:
@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);
}
}
Run Code Online (Sandbox Code Playgroud)
该#太棒了,我们编码为我所期望的,但/我们做不.我尝试使用builder.build(URLEncoder.encode(id)),但随后UriBuilder编码了%,所以你得到了.../string%252Fwith%252Fslashes!
在我看来,不一致的,它编码#和%而不是/,但我怀疑是有很好的理由对此我没有看到.所以我的问题是:
.../string%2Fwith%2Fslashes …在我发现的关于JAX-RS和缓存的少数几个问题(有答案)中,生成ETag(用于缓存)的答案是在Response对象上设置一些值.如下所示:
@GET
@Path("/person/{id}")
public Response getPerson(@PathParam("id") String name, @Context Request request){
Person person = _dao.getPerson(name);
if (person == null) {
return Response.noContent().build();
}
EntityTag eTag = new EntityTag(person.getUUID() + "-" + person.getVersion());
CacheControl cc = new CacheControl();
cc.setMaxAge(600);
ResponseBuilder builder = request.evaluatePreconditions(person.getUpdated(), eTag);
if (builder == null) {
builder = Response.ok(person);
}
return builder.cacheControl(cc).lastModified(person.getUpdated()).build();
}
Run Code Online (Sandbox Code Playgroud)
问题是对我们不起作用,因为我们对SOAP和REST服务使用相同的方法,通过使用@WebMethod(SOAP),@ GET(以及我们可能需要公开服务的任何其他方法)来注释方法.以前的服务对我们来说是这样的(不包括标题的创建):
@WebMethod
@GET
@Path("/person/{id}")
public Person getPerson(@WebParam(name="id") @PathParam("id") String name){
return _dao.getPerson(name);
}
Run Code Online (Sandbox Code Playgroud)
有没有办法 - 通过一些额外的配置 - 设置这些标题?这是我第一次发现使用Response对象实际上只比自动转换有一些好处...
我们正在使用Apache CXF.
我想DateTime在泽西岛使用Joda的查询参数,但泽西开箱即用不支持.我假设实现一个InjectableProvider是添加DateTime支持的正确方法.
可有人点我一个很好的实现的InjectableProvider对DateTime?或者是否有值得推荐的替代方法?(我知道我可以转换Date或String在我的代码,但是这似乎是一个更小的解决方案).
谢谢.
解:
我在下面修改了Gili的答案,使用@ContextJAX-RS中的注入机制而不是Guice.
更新:如果未在服务方法参数中注入UriInfo,则可能无法正常工作.
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider;
import java.util.List;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;
import org.joda.time.DateTime;
/**
* Enables DateTime to be used as a QueryParam.
* <p/>
* @author Gili Tzabari
*/
@Provider
public class DateTimeInjector extends PerRequestTypeInjectableProvider<QueryParam, DateTime>
{
private final UriInfo uriInfo;
/**
* Creates a new …Run Code Online (Sandbox Code Playgroud) 我有一个jax-rs服务,它接收路径中的一组参数pathparameters.这些参数可能是包含不适合url的值的字符串,因此它们在客户端使用java.net.UrlEncoder进行urlencoded,如下所示:
String param = URLEncoder.encode(o.toString(), "UTF-8");
Run Code Online (Sandbox Code Playgroud)
这用于构建URL supplier/group/param1/param2/param3.如果其中一个因urlencoding而被更改,例如,如果它只是一个空格,则在服务上接收的字符串是一个+符号.
@GET
@Path("{supplierId}/{groupCode}/{groupId}")
@Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML})
public SupplierGroup getSupplierGroup(@PathParam("supplierId") BigDecimal supplierId,
@PathParam("groupCode") String groupCode,
@PathParam("groupId") BigDecimal groupId) {
//now groupCode is "+", not " "
}
Run Code Online (Sandbox Code Playgroud)
我希望jaxrs能够自动解码编码路径参数.
编辑:测试更多我发现,当发送使用%20空间时,它能够解码参数.
我正面临以下问题.我已经花了3天多的时间,但找不到解决方案.请指导我在这里做错了什么.我是野生蝇新来的Resteasy.
这是堆栈跟踪
19:05:57,610 WARN [org.jboss.resteasy.core.ExceptionHandler] (default task-14) failed to execute: javax.ws.rs.NotFoundException: Could not find resource for full path: http://localhost:8080/admin-ws/services/user/getUser
at org.jboss.resteasy.core.registry.ClassNode.match(ClassNode.java:73) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.registry.RootClassNode.match(RootClassNode.java:48) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.ResourceMethodRegistry.getResourceInvoker(ResourceMethodRegistry.java:444) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.SynchronousDispatcher.getInvoker(SynchronousDispatcher.java:234) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:171) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:220) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51) [resteasy-jaxrs-3.0.8.Final.jar:]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [jboss-servlet-api_3.1_spec-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:113) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:56) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:45) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:70) …Run Code Online (Sandbox Code Playgroud) 我有一个异常映射器如下
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
@Provider
public class NotFoundMapper implements ExceptionMapper<NotFoundException> {
private final Logger log = LoggerFactory.getLogger(getClass());
private final MapperResponseBuilder responseBuilder = new MapperResponseBuilder();
@Override
public Response toResponse(NotFoundException ex) {
log.warn("NotFoundException : " + ex.getMessage(), ex);
return responseBuilder.buildErrorResponse(ex.getMessage(), Status.BAD_REQUEST);
}
}
Run Code Online (Sandbox Code Playgroud)
所以NotFoundException是一个RuntimeException.我想有3个异常映射器,它们映射
有没有办法优先考虑那些?
我想通过外部枚举定义从JAX-RS端点生成一个招摇,但生成的swagger直接包含枚举到模型的定义中.这意味着不会生成枚举文档,而是在客户端复制相同的枚举.
我使用swagger-jaxrs依赖项来扫描我的端点并生成swagger json文件.此GitHub 存储库可用于重现该问题.我还在swagger-core存储库上创建了一个GitHub 问题.
@Api("hello")
@Path("/helloSwagger")
public class HelloSwagger {
@ApiOperation(value = "Get all unique customers", notes = "Get all customers matching the given search string.", responseContainer = "Set", response = User.class)
@GET
@Path("/getUniqueUsers")
@Produces(MediaType.APPLICATION_JSON)
public Set<User> getUniqueUsers(
@ApiParam(value = "The search string is used to find customer by their name. Not case sensitive.") @QueryParam("search") String searchString,
@ApiParam(value = "Limits the size of the result set", defaultValue = "50") @QueryParam("limit") int limit
) { …Run Code Online (Sandbox Code Playgroud)