Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/376.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2008/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JAX-RS—;如何同时返回JSON和HTTP状态代码?_Java_Rest_Jax Rs_Http Status Codes - Fatal编程技术网

Java JAX-RS—;如何同时返回JSON和HTTP状态代码?

Java JAX-RS—;如何同时返回JSON和HTTP状态代码?,java,rest,jax-rs,http-status-codes,Java,Rest,Jax Rs,Http Status Codes,我正在编写一个RESTWeb应用程序(NetBeans 6.9、JAX-RS、TopLink Essentials),并试图返回JSON和HTTP状态代码。我已经准备好了代码,可以在从客户端调用HTTP GET方法时返回JSON。基本上: @Path("get/id") @GET @Produces("application/json") public M_機械 getMachineToUpdate(@PathParam("id") String id) { // some code

我正在编写一个RESTWeb应用程序(NetBeans 6.9、JAX-RS、TopLink Essentials),并试图返回JSON和HTTP状态代码。我已经准备好了代码,可以在从客户端调用HTTP GET方法时返回JSON。基本上:

@Path("get/id")
@GET
@Produces("application/json")
public M_機械 getMachineToUpdate(@PathParam("id") String id) {

    // some code to return JSON ...

    return myJson;
}
但我还想返回一个HTTP状态码(500200204等)以及JSON数据

我尝试使用
HttpServletResponse

response.sendError("error message", 500);
但这使浏览器认为它是“真正的”500,因此输出网页是一个常规的HTTP500错误页面


我想返回一个HTTP状态代码,这样我的客户端JavaScript就可以根据它处理一些逻辑(例如,在HTML页面上显示错误代码和消息)。这是可能的还是HTTP状态码不应该用于这种事情?

我没有使用JAX-RS,但我有一个类似的场景,我使用:

response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());

JAX-RS支持标准/自定义HTTP代码。请参见ResponseBuilder和ResponseStatus,例如:

请记住,JSON信息更多地是关于与资源/应用程序关联的数据。HTTP代码更多地是关于被请求的CRUD操作的状态。(至少在REST-ful系统中应该是这样的)

下面是一个示例:

@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();
}
看看这门课


请注意,您应该始终指定一个内容类型,特别是当您传递多个内容类型时,但是如果每条消息都将表示为JSON,则您可以使用
@products(“application/JSON”)
对该方法进行注释,以他的身份进行回答即可,但它修改了整个方法,让Jackson+JAXB之类的提供者自动将返回的对象转换为JSON之类的输出格式。受ApacheCXF(使用特定于CXF的类)的启发,我找到了一种设置响应代码的方法,该代码可以在任何JAX-RS实现中工作:注入HttpServletResponse上下文并手动设置响应代码。例如,以下是如何在适当时将响应代码设置为
CREATED

@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
}
改进:在找到另一个相关的后,我了解到可以将
HttpServletResponse
作为成员变量注入,即使对于单例服务类(至少在RESTEasy中)!!这是一种比用实现细节污染API好得多的方法。它看起来是这样的:

@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
}
@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();
    }
}

如果您想因为异常而更改状态代码,那么使用JAX-RS2.0可以实现这样一个ExceptionMapper。这将为整个应用程序处理此类异常

@Provider
public class UnauthorizedExceptionMapper implements ExceptionMapper<EJBAccessException> {

    @Override
    public Response toResponse(EJBAccessException exception) {
        return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build();
    }

}
@Provider
公共类UnauthorizedExceptionMapper实现ExceptionMapper{
@凌驾
公共响应响应(EJBAccessException异常){
返回Response.status(Response.status.UNAUTHORIZED.getStatusCode()).build();
}
}

如果您的WS-RS需要引发错误,为什么不使用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;
}

请看这里的示例,它最好地说明了问题以及如何在最新(2.3.1)版本的Jersey中解决该问题


它基本上涉及定义一个自定义异常并将返回类型保留为实体。当出现错误时,将引发异常,否则,将返回POJO。

在REST web服务中设置HTTP状态代码有几个用例,并且至少有一个在现有答案中没有充分记录(即,当您使用JAXB使用自动魔法JSON/XML序列化时,您希望返回要序列化的对象,但也返回与默认值不同的状态代码)

因此,让我尝试列举不同的用例和每个用例的解决方案:

1.错误代码(500404,…) 当您希望返回不同于
200 OK
的状态代码时,最常见的使用情况是发生错误时

例如:

  • 请求了一个实体,但它不存在(404)
  • 请求在语义上不正确(400)
  • 用户未被授权(401)
  • 数据库连接有问题(500)
  • 等等
a) 抛出异常 在这种情况下,我认为处理这个问题最干净的方法就是抛出一个异常。此异常将由一个
异常映射器
处理,该映射器将异常转换为带有相应错误代码的响应

您可以使用Jersey预先配置的默认
ExceptionMapper
(我想其他实现也是如此)并抛出
javax.ws.rs.WebApplicationException的任何现有子类。这些是预定义的异常类型,预先映射到不同的错误代码,例如:

  • BadRequestException(400)
  • InternalServerErrorException(500)
  • NotFoundException(404)
等等。您可以在此处找到列表:

或者,您可以定义自己的自定义异常和
ExceptionMapper
类,并通过
@Provider
注释()将这些映射器添加到Jersey:

提供者:

    @Provider
    public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException> 
    {
        @Override
        public Response toResponse(MyApplicationException exception) 
        {
            return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();  
        }
    }
2.成功,但不是200 要设置返回状态的另一种情况是,操作成功,但要返回不同于200的成功代码,以及在正文中返回的内容

一个常见的用例是当您创建一个新实体(
POST
request)并希望返回关于这个新实体的信息,或者可能是实体本身的信息,以及
201创建的状态代码

一种方法是像上面描述的那样使用响应对象,并自己设置请求的主体。但是,这样做会使您失去使用JAXB提供的XML或JSON自动序列化的能力

这是返回实体对象的原始方法,该实体对象将由JAXB序列化为JSON:

@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
    User newuser = ... do something like DB insert ...
    return newuser;
}
这将返回新创建用户的JSON表示,但返回
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
    User newuser = ... do something like DB insert ...
    return newuser;
}
@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;
}
@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();
}
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();
}
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;
        }
      }
    }
  }
}
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;
  }
}
@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();
    }
}
public SkuResultListDTO getSkuData()
    ....
return SkuResultListDTO;
public Response getSkuData()
...
return Response.status(Response.Status.FORBIDDEN).entity(dfCoreResultListDTO).build();
    <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>
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);
    }
}