JAX-RS - JSON 및 HTTP 상태 코드를 함께 반환하려면 어떻게 해야 합니까?
REST 웹 앱(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;
}
그러나 JSON 데이터와 함께 HTTP 상태 코드(500, 200, 204 등)도 반환하고 싶습니다.
사용하려고 했습니다.HttpServletResponse
:
response.sendError("error message", 500);
그러나 이로 인해 브라우저는 이것이 "진짜" 500이라고 생각하게 되었고, 출력 웹 페이지는 일반적인 HTTP 500 오류 페이지였습니다.
클라이언트측 JavaScript가 일부 로직을 처리할 수 있도록 HTTP 상태 코드를 반환하고 싶다(예를 들어 오류 코드와 메시지를 HTML 페이지에 표시).이것이 가능한가, 아니면 HTTP 상태 코드를 사용하지 않는 것이 좋은가.
다음은 예를 제시하겠습니다.
@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으로 을 붙일 수 .@Produces("application/json")
REST 웹 서비스에서 HTTP 상태 코드를 설정하는 몇 가지 사용 사례가 있으며, 적어도 하나는 기존 답변에 충분히 문서화되어 있지 않습니다(즉, JAXB를 사용하여 자동 매거진 JSON/XML 직렬화를 사용하고 있으며 직렬화할 개체를 반환하고 기본 200과 다른 상태 코드를 반환하는 경우).
각 사용 사례와 솔루션을 열거해 보겠습니다.
1. 에러 코드 (500, 404, ...)
때 예200 OK
에러가 발생했을 때입니다.
예를 들어 다음과 같습니다.
- 엔티티가 요구되지만 존재하지 않습니다(404).
- 요구가 의미적으로 올바르지 않다(400)
- 사용자에게 권한이 없습니다(401).
- 데이터베이스 연결에 문제가 있습니다(500).
- 기타.
a) 예외 발생
이 경우 예외를 두는 것이 가장 깔끔한 대처법이라고 생각합니다.는 이음음 음음음 음음음 음음음 this this this this this에 의해 됩니다.ExceptionMapper
이 예외는 적절한 에러 코드를 가진 응답으로 변환됩니다.
사용 .ExceptionMapper
Jersey와 함께 사전 구성되며(다른 구현에서도 마찬가지일 수 있음), 기존 서브클래스의 어느 쪽이든 사용할 수 있습니다.javax.ws.rs.WebApplicationException
다음은 다른 에러 코드에 미리 매핑되어 있는 사전 정의된 예외 유형입니다.하다
- 불량 요구예외(400)
- Internal Server Error Exception(500)
- Not Found Exception(404)
기타 목록은 API에서 확인할 수 있습니다.
및 사용자 정의 예외를 .ExceptionMapper
합니다.@Provider
주석(이 예의 소스):
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
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
@Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
주의: 사용하는 기존 예외 유형에 대해 Exception Mappers를 작성할 수도 있습니다.
b) Response Builder 사용
하는 또 다른 은 상태 코드를 입니다.Response
원하는 코드를 사용하여 응답을 작성합니다.
이 경우 메서드의 반환 유형은 다음과 같아야 합니다.javax.ws.rs.core.Response
은 '의 'accepted '와 'hisdrewness'의 'accepted answere'는
@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. 성공하지만 200은 아니다
반환 상태를 설정하는 다른 예로는 작업이 성공했지만 본문에 반환되는 내용과 함께 200이 아닌 성공 코드를 반환하는 경우가 있습니다.
하는 경우는 새로운 엔티티를입니다.POST
새로운엔티티 .201 Created
상태 코드
한 가지 방법은 위에서 설명한 대로 응답 개체를 사용하여 요청 본문을 직접 설정하는 것입니다.그러나 이렇게 하면 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 표현이 반환되지만 반환 상태는 201이 아닌 200이 됩니다.
는 프라이빗에서 새로운 것을 사용하는 입니다.Response
를 설정하려면 , 」를 가 있습니다.Response
오브젝트를 지정합니다. 하나요?User
연재할 오브젝트
a) servlet 응답에 코드를 설정합니다.
이를 해결하기 위한 한 가지 방법은 Garet Wilson의 답변에서 설명한 것처럼 servlet request 객체를 가져와 응답 코드를 직접 수동으로 설정하는 것입니다.
@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;
}
메서드는 여전히 엔티티 개체를 반환하며 상태 코드는 201입니다.
이걸 작동시키려면 응답을 플러싱해야 한다는 걸 명심하세요.이것은 당사의 훌륭한 JAX_RS 리소스에 있는 저레벨 Servlet API 코드의 불쾌한 부활이며, 더 나쁜 것은 헤더가 이미 회선으로 전송되었기 때문에 이 후에 헤더를 수정할 수 없게 된다는 것입니다.
b) 엔티티와 함께 응답 오브젝트를 사용한다.
이 경우 가장 좋은 해결책은 Response 개체를 사용하여 이 응답 개체에서 직렬화할 엔티티를 설정하는 것입니다.이 경우 Response 객체를 범용으로 하여 페이로드 엔티티의 유형을 나타내면 좋지만, 현재는 그렇지 않습니다.
@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();
}
이 경우 Response Builder 클래스의 작성된 메서드를 사용하여 상태 코드를 201로 설정합니다.엔티티 오브젝트(사용자)를 entity() 메서드를 통해 응답에 전달합니다.
그 결과 HTTP 코드는 우리가 원하는 대로 401이며, 응답 본문은 우리가 User 객체를 반환했을 때와 완전히 같은 JSON이 됩니다.로케이션 헤더도 추가합니다.
Response 클래스에는 다음과 같은 다양한 상태(stati?)에 대한 다수의 빌더 메서드가 있습니다.
Response.accepted() Response.ok() Response.noContent() Response.notAcceptable()
NB: hateoas 개체는 리소스 URI 생성을 지원하기 위해 개발한 도우미 클래스입니다.여기서 독자적인 메카니즘을 책정할 필요가 있습니다.
이상입니다.
이 장황한 답변이 누군가에게 도움이 되었으면 합니다.
그의 둔감한 답변은 유효하지만, Jackson+와 같은 프로바이더에 대한 전체적인 접근 방식을 변경합니다.JAXB는 반환된 객체를 JSON 등의 출력 형식으로 자동 변환합니다.Apache CXF 포스트(CXF 고유의 클래스를 사용)에서 영감을 받아 JAX-RS 구현에서 동작하는 응답 코드를 설정하는 방법을 찾았습니다.Http Servlet Response 컨텍스트를 삽입하고 응답 코드를 수동으로 설정합니다.예를 들어, 다음과 같이 응답 코드를 설정하는 방법을 나타냅니다.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
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★!이는 구현 세부 정보로 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
}
의 청결을 하고 Response
objects objects objects 、 objects objects 、 objects objects objects objects objects objects objects objects objects objects objects 。@NameBinding
구현에 대한 구속력을 가집니다.ContainerResponseFilter
.
주석의 요점은 다음과 같습니다.
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;
}
}
예외로 인해 상태 코드를 변경하고 싶은 경우 JAX-RS 2.0을 사용하면 다음과 같은 Exception Mapper를 구현할 수 있습니다.앱 전체에 대해 이러한 예외를 처리합니다.
@Provider
public class UnauthorizedExceptionMapper implements ExceptionMapper<EJBAccessException> {
@Override
public Response toResponse(EJBAccessException exception) {
return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build();
}
}
WS-RS에서 오류가 발생할 경우 Web Application만 사용하면 됩니다.예외?
@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;
}
이렇게 코드를 반복한 json 메시지를 작성하는 것도 매우 유용했습니다.
@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-RS는 표준/커스텀HTTP 코드를 지원합니다.ResponseBuilder 및 ResponseStatus를 참조하십시오.예:
JSON 정보는 리소스/애플리케이션과 관련된 데이터에 더 가깝습니다.HTTP 코드는 요구되고 있는 CRUD 동작의 상태에 관한 것입니다.(적어도 REST-ful 시스템에서는 그렇게 해야 합니다.)
이 예에서는 Jersey의 최신 버전(2.3.1)에서의 문제 및 해결 방법을 가장 잘 설명하고 있습니다.
https://jersey.java.net/documentation/latest/representations.html#d0e3586
기본적으로 커스텀 예외를 정의하고 반환 유형을 엔티티로 유지합니다.오류가 발생하면 예외가 느려지고 그렇지 않으면 POJO가 반환됩니다.
JAX-RS를 사용하고 있지 않지만, 다음과 같은 시나리오가 있습니다.
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
또, 디폴트에서는, Http 코드 400 이상의 경우는, Jersey 가 응답 본문을 덮어씁니다.
지정된 엔티티를 응답 본체로 가져오려면 web.xml Configuration파일에서 다음 init-param을 Jersey에 추가합니다.
<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>
다음 코드가 나에게 효과가 있었다.주석이 달린 setter를 통해 messageContext를 삽입하고 상태 코드를 "추가" 방식으로 설정합니다.
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);
}
}
Microfile Open을 통한 Nthalk의 답변API는 @APIResponse 주석을 사용하여 반환 코드를 문서와 일치시킬 수 있습니다.
이를 통해 다음과 같은 JAX-RS 메서드에 태그를 지정할 수 있습니다.
@GET
@APIResponse(responseCode = "204")
public Resource getResource(ResourceRequest request)
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);
}
}
}
}
}
다음과 같이 여러 개의 주석을 메서드에 추가할 때 경고가 발생합니다.
@APIResponse(responseCode = "201", description = "first use case")
@APIResponse(responseCode = "204", description = "because you can")
public Resource getResource(ResourceRequest request)
메시지 본문 리더 및 라이터와 함께 저지 2.0을 사용하고 있습니다.메시지 본문 라이터 구현에서도 사용되는 특정 엔티티로서 메서드 반환 타입을 사용하고, 같은 pojo, SkuListDTO.@GET @Consumes({"application/xml", "application/json"), @Products({"application/xml", "application/json") @Resu(Sku)를 반환하고 있었습니다.
public SkuResultListDTO getSkuData()
....
return SkuResultListDTO;
내가 바꾼 건 이것뿐이었고, 작가 실장은 내버려뒀고, 그것은 여전히 작동했다.
public Response getSkuData()
...
return Response.status(Response.Status.FORBIDDEN).entity(dfCoreResultListDTO).build();
언급URL : https://stackoverflow.com/questions/4687271/jax-rs-how-to-return-json-and-http-status-code-together
'programing' 카테고리의 다른 글
printf를 사용하여 문자를 반복하는 방법 (0) | 2022.08.15 |
---|---|
RegisterServiceWorker.js에서 Vue 앱 $refs.components 또는 Vuex $store 메서드를 호출하는 방법 (0) | 2022.08.14 |
Java 코드에서 UML 다이어그램(특히 시퀀스 다이어그램)을 생성하려면 어떻게 해야 합니까? (0) | 2022.08.14 |
Z/OS에서 C++의 C 소켓 API를 사용하는 방법 (0) | 2022.08.14 |
모바일의 MS 팀 내 인증 탭 (0) | 2022.08.14 |