JAX-RS - วิธีคืนรหัสสถานะ JSON และ HTTP ด้วยกันได้อย่างไร


248

ฉันกำลังเขียนแอปเว็บ REST (NetBeans 6.9, JAX-RS, TopLink Essentials) และพยายามส่งคืนรหัสสถานะJSON และ HTTP ฉันมีโค้ดที่พร้อมใช้งานและส่งคืน JSON เมื่อเมธอด HTTP GET ถูกเรียกใช้จากไคลเอ็นต์ เป็นหลัก:

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

    // some code to return JSON ...

    return myJson;
}

แต่ผมยังต้องการที่จะกลับรหัสสถานะ HTTP (500, 200, 204, ฯลฯ ) พร้อมกับข้อมูล JSON

ฉันพยายามใช้HttpServletResponse:

response.sendError("error message", 500);

แต่สิ่งนี้ทำให้เบราว์เซอร์คิดว่าเป็น "ของจริง" 500 ดังนั้นหน้าผลลัพธ์จึงเป็นหน้าข้อผิดพลาด HTTP 500 ปกติ

ฉันต้องการส่งคืนรหัสสถานะ HTTP เพื่อให้ JavaScript ฝั่งไคลเอ็นต์ของฉันสามารถจัดการกับตรรกะบางอย่างขึ้นอยู่กับมัน (เช่นเช่นแสดงรหัสข้อผิดพลาดและข้อความในหน้า HTML) เป็นไปได้หรือไม่ควรใช้รหัสสถานะ HTTP สำหรับสิ่งนั้น


2
อะไรคือความแตกต่างระหว่าง 500 ที่คุณต้องการ (ไม่จริง? :)) และ 500 จริง
มีดโกน

@razor ที่นี่จริง 500 หมายถึงหน้าข้อผิดพลาด HTML แทนการตอบสนอง JSON
Nupur

เว็บเบราว์เซอร์ไม่ได้ออกแบบมาเพื่อทำงานกับ JSON แต่มีหน้า HTML ดังนั้นหากคุณตอบสนองด้วย 500 (และแม้กระทั่งเนื้อหาข้อความบางส่วน) เบราว์เซอร์สามารถแสดงข้อความข้อผิดพลาด (ขึ้นอยู่กับการใช้เบราว์เซอร์) จริงๆ ผู้ใช้ปกติ
มีดโกน

คำตอบ:


347

นี่คือตัวอย่าง:

@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")


12
มันใช้งานได้ แต่สิ่งที่ฉันไม่ชอบเกี่ยวกับค่าตอบกลับคือฉันคิดว่ามันทำให้รหัสของคุณสกปรกโดยเฉพาะเกี่ยวกับลูกค้าที่พยายามใช้ หากคุณให้อินเทอร์เฟซที่ส่งคืนการตอบกลับไปยังบุคคลที่สามเขาจะไม่ทราบว่าคุณกำลังกลับมาประเภทใด Spring ทำให้คำอธิบายประกอบชัดเจนยิ่งขึ้นมีประโยชน์มากถ้าคุณส่งคืนรหัสสถานะเสมอ (เช่น HTTP 204)
Guido

19
การทำให้ class ทั่วไป (Response <T>) น่าจะเป็นการปรับปรุงที่น่าสนใจสำหรับ jax-rs เพื่อให้ได้ข้อดีของทั้งสองทางเลือก
กุยโด

41
ไม่จำเป็นต้องแปลงเอนทิตีเป็น json อย่างใด คุณสามารถใช้return Response.status(Response.Status.Forbidden).entity(myPOJO).build();งานได้เหมือนว่าคุณจะreturn myPOJO;ได้ แต่ด้วยการตั้งค่าเพิ่มเติมของรหัสสถานะ HTTP
kratenko

1
ฉันคิดว่าการแยกตรรกะทางธุรกิจออกเป็นระดับบริการที่แยกจากกันทำงานได้ดี ปลายทางใช้การตอบกลับเป็นประเภทการส่งคืนและเมธอดส่วนใหญ่เป็นเพียงการเรียกใช้วิธีการบริการรวมถึงคำอธิบายประกอบพา ธ และพารามิเตอร์ มันแยกตรรกะออกจากการแมปประเภท URL / เนื้อหาอย่างชัดเจน (ที่ยางกระทบถนนเพื่อพูด)
Stijn de Witt

จริงๆแล้วเราสามารถส่งคืนวัตถุที่ไม่ห่อหุ้มไปยังการตอบสนองได้
ses

192

มีหลายกรณีการใช้งานสำหรับการตั้งค่ารหัสสถานะ HTTP ในบริการเว็บ REST และอย่างน้อยหนึ่งรายการไม่ได้รับการบันทึกไว้อย่างเพียงพอในคำตอบที่มีอยู่ (เช่นเมื่อคุณกำลังใช้การจัดลำดับ JSON / XML อัตโนมัติ - เวทมนต์โดยใช้ JAXB และคุณต้องการส่งคืน วัตถุที่จะต่อเนื่อง แต่ยังรหัสสถานะที่แตกต่างจาก 200 เริ่มต้น)

ดังนั้นฉันจะลองและแจกแจงกรณีการใช้งานที่แตกต่างกันและวิธีแก้ปัญหาสำหรับแต่ละกรณี:

1. รหัสข้อผิดพลาด (500, 404, ... )

กรณีการใช้งานที่พบบ่อยที่สุดเมื่อคุณต้องการส่งคืนรหัสสถานะที่แตกต่างจาก200 OKเมื่อเกิดข้อผิดพลาด

ตัวอย่างเช่น:

  • มีการร้องขอเอนทิตี แต่ไม่มีอยู่ (404)
  • คำขอไม่ถูกต้องเชิงความหมาย (400)
  • ผู้ใช้ไม่ได้รับอนุญาต (401)
  • มีปัญหากับการเชื่อมต่อฐานข้อมูล (500)
  • ฯลฯ ..

a) โยนข้อยกเว้น

ในกรณีนี้ฉันคิดว่าวิธีที่สะอาดที่สุดในการจัดการปัญหาคือการยกเว้น ข้อยกเว้นนี้จะได้รับการจัดการโดยExceptionMapperที่จะแปลข้อยกเว้นเป็นการตอบกลับด้วยรหัสข้อผิดพลาดที่เหมาะสม

คุณสามารถใช้ค่าเริ่มต้นExceptionMapperที่มาก่อนกำหนดที่มีย์ (และผมคิดว่ามันเป็นเรื่องเดียวกันกับการใช้งานอื่น ๆ ) และโยนใด ๆ javax.ws.rs.WebApplicationExceptionของที่มีอยู่ย่อยชั้นเรียนของ เหล่านี้เป็นประเภทข้อยกเว้นที่กำหนดไว้ล่วงหน้าที่แมปล่วงหน้าไปยังรหัสข้อผิดพลาดที่แตกต่างกันตัวอย่างเช่น:

  • BadRequestException (400)
  • InternalServerErrorException (500)
  • NotFoundException (404)

เป็นต้นคุณสามารถค้นหารายการได้ที่นี่: API

อีกทางหนึ่งคุณสามารถกำหนดข้อยกเว้นและExceptionMapperคลาสที่กำหนดเองของคุณเองและเพิ่มตัวแม็พเหล่านี้ไปยัง Jersey ด้วยค่าเฉลี่ยของ@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();  
        }
    }

หมายเหตุ: คุณยังสามารถเขียน ExceptionMappers สำหรับประเภทข้อยกเว้นที่มีอยู่ที่คุณใช้

b) ใช้เครื่องมือสร้างการตอบสนอง

อีกวิธีในการตั้งค่ารหัสสถานะคือการใช้ตัวResponseสร้างเพื่อสร้างการตอบกลับด้วยรหัสที่ต้องการ

javax.ws.rs.core.Responseในกรณีที่ประเภทกลับวิธีการของคุณต้องเป็น นี่คือคำอธิบายในการตอบกลับอื่น ๆ เช่นคำตอบที่ยอมรับได้ของเขาและดูเหมือนว่า:

@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รหัสสถานะ

วิธีหนึ่งคือการใช้วัตถุตอบสนองเช่นเดียวกับที่อธิบายไว้ข้างต้นและตั้งค่าเนื้อหาของคำขอด้วยตนเอง อย่างไรก็ตามการทำเช่นนี้ทำให้คุณไม่สามารถใช้การจัดลำดับอัตโนมัติเป็น XML หรือ JSON ที่ JAXB จัดหาให้

นี่เป็นวิธีการดั้งเดิมที่ส่งคืนวัตถุเอนทิตีที่จะถูกทำให้เป็นอนุกรมกับ JSON โดย JAXB:

@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 ของผู้ใช้ที่สร้างขึ้นใหม่ แต่สถานะการส่งคืนจะเป็น 200 ไม่ใช่ 201

ตอนนี้ปัญหาคือถ้าฉันต้องการใช้ตัวResponseสร้างเพื่อตั้งรหัสส่งคืนฉันต้องส่งคืนResponseวัตถุในวิธีการของฉัน ฉันจะกลับยังไงUserวัตถุให้เป็นอนุกรมได้อย่างไร

a) ตั้งรหัสบนการตอบสนองของเซิร์ฟเล็ต

วิธีหนึ่งในการแก้ปัญหานี้คือการขอรับวัตถุคำขอ servlet และตั้งรหัสการตอบสนองด้วยตนเองเช่นแสดงในคำตอบของ Garett Wilson

@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

โปรดทราบว่าเพื่อให้มันทำงานได้ฉันต้องล้างการตอบสนอง นี่คือการฟื้นตัวที่ไม่พึงประสงค์ของรหัส Servlet API ระดับต่ำในแหล่งข้อมูล JAX_RS ที่ดีของเราและที่แย่กว่านั้นคือทำให้ส่วนหัวไม่สามารถแก้ไขได้หลังจากสิ่งนี้เพราะพวกเขาถูกส่งไปที่สายแล้ว

b) ใช้ออบเจคการตอบสนองกับเอนทิตี

ทางออกที่ดีที่สุดในกรณีนั้นคือการใช้การตอบสนองวัตถุและตั้งเอนทิตีที่จะต่อเนื่องกันบนวัตถุการตอบสนองนี้ มันเป็นการดีที่จะทำให้การตอบสนองวัตถุทั่วไปเพื่อระบุประเภทของหน่วยงานที่บรรจุในกรณีนั้น แต่ไม่ได้เป็นกรณีในปัจจุบัน

@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 เราส่งวัตถุเอนทิตี (ผู้ใช้) ไปยังการตอบกลับผ่านเมธอดเอนทิตี้ ()

ผลลัพธ์ก็คือรหัส HTTP คือ 401 ตามที่เราต้องการและเนื้อความของการตอบสนองเป็น JSON เดียวกับที่เราเคยมีมาก่อนเมื่อเราเพิ่งคืนค่าวัตถุผู้ใช้ นอกจากนี้ยังเพิ่มส่วนหัวสถานที่ตั้ง

คลาส Response มีจำนวนวิธีการสร้างสำหรับสถานะที่แตกต่าง (stati?) เช่น:

Response.accepted () Response.ok () Response.noContent () Response.notAcceptable ()

หมายเหตุ: วัตถุเกลียดชังเป็นคลาสผู้ช่วยที่ฉันพัฒนาขึ้นเพื่อช่วยสร้าง URI ของทรัพยากร คุณจะต้องมีกลไกของคุณเองที่นี่;)

เกี่ยวกับมัน.

ฉันหวังว่าการตอบสนองที่ยาวนานนี้จะช่วยใครซักคน :)


ฉันสงสัยว่ามีวิธีที่สะอาดเพื่อส่งคืนวัตถุข้อมูลแทนการตอบกลับหรือไม่ flushสกปรกแน่นอน
AlikElzin-kilaka

1
เป็นเพียงสัตว์เลี้ยงฉุนเฉียวของฉัน: 401 ไม่ได้หมายความว่าผู้ใช้จะไม่ได้รับอนุญาต หมายความว่าลูกค้าไม่ได้รับอนุญาตเพราะเซิร์ฟเวอร์ไม่ทราบว่าคุณเป็นใคร หากผู้ใช้ที่เข้าสู่ระบบ / เป็นที่รู้จักไม่ได้รับอนุญาตให้ดำเนินการบางอย่างรหัสตอบกลับที่ถูกต้องคือ 403 สิ่งต้องห้าม
Demonblack

69

คำตอบของเขาจะทำงานได้ แต่จะปรับเปลี่ยนวิธีการทั้งหมดเพื่อให้ผู้ให้บริการเช่น Jackson + JAXB แปลงวัตถุที่คุณส่งคืนเป็นรูปแบบผลลัพธ์เช่น JSON โดยอัตโนมัติ ได้รับแรงบันดาลใจจากโพสต์ Apache CXF (ซึ่งใช้คลาสเฉพาะ 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
}

คุณสามารถรวมวิธีการได้จริง: ใส่คำอธิบายประกอบด้วย@Producesและไม่ระบุประเภทสื่อในขั้นสุดท้ายResponse.okและคุณจะได้รับวัตถุส่งคืนของคุณอย่างถูกต้อง JAXB ต่อเนื่องเป็นประเภทสื่อที่เหมาะสมเพื่อให้ตรงกับคำขอ (ฉันเพิ่งลองวิธีนี้ด้วยวิธีเดียวที่สามารถส่งคืน XML หรือ JSON: วิธีการนั้นไม่จำเป็นต้องพูดถึงเช่นกันยกเว้นใน@Producesคำอธิบายประกอบ)
Royston Shufflebotham

คุณถูก Garret ตัวอย่างของฉันเป็นตัวอย่างของการเน้นที่การให้ประเภทเนื้อหา วิธีการของเราคล้ายกัน แต่แนวคิดของการใช้MessageBodyWriterและProviderอนุญาตให้มีการเจรจาต่อรองเนื้อหาโดยนัยแม้ว่าดูเหมือนว่าตัวอย่างของคุณจะขาดรหัสบางส่วน นี่คือคำตอบอื่นที่ฉันให้ไว้ซึ่งแสดงสิ่งนี้: stackoverflow.com/questions/5161466/…
hisdrewness ของเขา

8
response.setStatus()ฉันไม่สามารถที่จะแทนที่รหัสสถานะใน วิธีเดียวที่จะส่งตัวอย่างการตอบสนอง404 ไม่พบคือการตั้งค่ารหัสสถานะการตอบสนองresponse.setStatus(404)แล้วปิดกระแสเอาต์พุตresponse.getOutputStream().close()เพื่อ JAX-RS ไม่สามารถรีเซ็ตสถานะของฉัน
Rob Juurlink

2
ฉันสามารถใช้วิธีนี้เพื่อตั้งค่ารหัส 201 แต่ต้องเพิ่มบล็อกแบบลองจับด้วยresponse.flushBuffer()เพื่อหลีกเลี่ยงเฟรมเวิร์กที่แทนที่รหัสตอบกลับของฉัน ไม่ค่อยสะอาด
Pierre Henry

1
@RobJuurlink ถ้าคุณต้องการโดยเฉพาะเพื่อส่งกลับก็อาจจะง่ายขึ้นเพียงเพื่อการใช้งาน404 Not Found throw new NotFoundException()
Garret Wilson

34

หากคุณต้องการให้ชั้นทรัพยากรของคุณสะอาดของResponseวัตถุแล้วผมขอแนะนำให้คุณใช้และมีผลผูกพันในการใช้งานของ@NameBindingContainerResponseFilter

นี่คือเนื้อของคำอธิบายประกอบ:

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;
  }
}

ช่วยให้ API สะอาดเป็นคำตอบที่ดี เป็นไปได้ไหมที่จะกำหนดขอบเขตคำอธิบายประกอบของคุณเช่น @Status (รหัส = 205) และให้ตัวดักจับแทนที่รหัสด้วยสิ่งที่คุณระบุ? ฉันคิดว่าโดยทั่วไปจะให้คำอธิบายประกอบแก่คุณเพื่อเอาชนะรหัสเมื่อใดก็ตามที่คุณต้องการ
2800708

@ user2800708 ฉันทำสิ่งนี้กับรหัสท้องถิ่นของฉันแล้วอัปเดตคำตอบตามที่คุณแนะนำ
Nthalk

ขอบคุณมาก ด้วยวิธีการนี้และเทคนิคอื่น ๆ ไม่กี่ฉันตอนนี้ฉันสามารถทำความสะอาด REST APIs ในรหัสของฉันเพื่อให้เป็นไปตามอินเทอร์เฟซ Java ง่าย ๆ โดยไม่พูดถึง REST ในนั้น มันเป็นเพียงกลไก RMI อื่น
2800708

6
แทนที่จะวนลูปสำหรับคำอธิบายประกอบใน StatusFilter คุณสามารถใส่คำอธิบายประกอบตัวกรองด้วย @ Status เพิ่มเติมจาก @ Provider จากนั้นตัวกรองจะถูกเรียกใช้บนทรัพยากรที่มีคำอธิบายประกอบด้วย @ สถานะเท่านั้น นี่คือจุดประสงค์ของ @ NameBinding
trevorism

1
คำบรรยายภาพดี @revorism มีอยู่คนหนึ่งผลข้างเคียงที่ไม่ให้ความสุขของการเป็น annotating StatusFilterกับ@Status: คุณอาจจำเป็นต้องจัดหาเริ่มต้นสำหรับบันทึกย่อของvalueข้อมูลหรือประกาศเมื่อ annotating ชั้นเรียน (เช่น: @Status(200)) เห็นได้ชัดว่าไม่เหมาะ
Phil

6

ในกรณีที่คุณต้องการเปลี่ยนรหัสสถานะเนื่องจากข้อยกเว้นด้วย JAX-RS 2.0 คุณสามารถใช้ ExceptionMapper เช่นนี้ สิ่งนี้จัดการข้อยกเว้นประเภทนี้สำหรับแอปทั้งหมด

@Provider
public class UnauthorizedExceptionMapper implements ExceptionMapper<EJBAccessException> {

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

}

6

หาก 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;
}

4
ในความคิดของฉัน WebApplicationExceptions ไม่เหมาะสำหรับข้อผิดพลาดด้านลูกค้าเพราะพวกเขาทิ้งร่องรอยกองใหญ่ ข้อผิดพลาดไคลเอนต์ไม่ควรทิ้งร่องรอยสแต็กฝั่งเซิร์ฟเวอร์และสร้างมลพิษการบันทึกด้วย
Rob Juurlink

5

JAX-RS รองรับรหัส HTTP มาตรฐาน / กำหนดเอง ดู ResponseBuilder และ ResponseStatus ตัวอย่างเช่น:

http://jackson.codehaus.org/javadoc/jax-rs/1.0/javax/ws/rs/core/Response.ResponseBuilder.html#status%28javax.ws.rs.core.Response.Status%29

โปรดทราบว่าข้อมูล JSON เป็นข้อมูลเพิ่มเติมเกี่ยวกับข้อมูลที่เกี่ยวข้องกับทรัพยากร / แอปพลิเคชัน รหัส HTTP เพิ่มเติมเกี่ยวกับสถานะของการดำเนินการ CRUD ที่ต้องการ (อย่างน้อยนั่นคือวิธีที่ควรจะอยู่ในระบบ REST-ful)


ลิงก์เสียหาย
Umpa

5

ฉันพบว่ามีประโยชน์มากในการสร้างข้อความ 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();
    }
}

4

โปรดดูตัวอย่างที่นี่มันแสดงให้เห็นถึงปัญหาที่ดีที่สุดและวิธีการแก้ไขในเจอร์ซีย์รุ่นล่าสุด (2.3.1)

https://jersey.java.net/documentation/latest/representations.html#d0e3586

โดยทั่วไปจะเกี่ยวข้องกับการกำหนดข้อยกเว้นแบบกำหนดเองและรักษาชนิดส่งคืนเป็นเอนทิตี เมื่อมีข้อผิดพลาดข้อยกเว้นจะถูกโยนมิฉะนั้นคุณจะส่งคืน POJO


ฉันต้องการเพิ่มว่าตัวอย่างที่น่าสนใจคือสิ่งที่พวกเขากำหนดคลาสข้อยกเว้นของตนเองและสร้างResponseในนั้น เพียงแค่มองหาCustomNotFoundExceptionชั้นเรียนและอาจจะคัดลอกไปยังโพสต์ของคุณ
JBert

ฉันใช้วิธีการนี้เพื่อหาข้อผิดพลาดและฉันชอบ แต่มันไม่สามารถใช้ได้กับรหัสสำเร็จ (ต่างจาก 200) เช่น '201 สร้าง'
Pierre Henry

3

ฉันไม่ได้ใช้ JAX-RS แต่ฉันมีสถานการณ์คล้ายกันที่ฉันใช้:

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

มันทำให้ฉันใช้ Spring MVC แต่มีวิธีที่ง่ายในการค้นหา!
หมวก

1

นอกจากนี้โปรดสังเกตว่าโดยค่าเริ่มต้น Jersey จะแทนที่เนื้อความการตอบกลับในกรณีที่เป็นรหัส http 400 หรือมากกว่า

ในการรับเอนทิตีที่ระบุของคุณเป็นเนื้อหาการตอบสนองลองเพิ่ม init-param ต่อไปนี้ลงใน Jersey ของคุณในไฟล์กำหนดค่า web.xml ของคุณ:

    <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>

0

รหัสต่อไปนี้ทำงานให้ฉัน การฉีด messageContext ผ่าน setter ที่มีคำอธิบายประกอบและตั้งรหัสสถานะในเมธอด "เพิ่ม" ของฉัน

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

0

การขยายคำตอบของNthalkด้วยMicroprofile OpenAPIคุณสามารถปรับรหัสส่งคืนให้ตรงกับเอกสารของคุณโดยใช้คำอธิบายประกอบ@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) 

-1

ฉันใช้ jersey 2.0 กับตัวอ่านข้อความและนักเขียน ฉันมีชนิดวิธีการคืนของฉันเป็นนิติบุคคลเฉพาะซึ่งยังใช้ในการดำเนินงานของนักเขียนข้อความและฉันก็กลับ pojo เดียวกัน SkuListDTO @GET @Consumes ({"application / xml", "application / json"}) @Produces ({"application / xml", "application / json"}) @Path ("/ skuResync")

public SkuResultListDTO getSkuData()
    ....
return SkuResultListDTO;

สิ่งที่ฉันเปลี่ยนไปคือสิ่งนี้ฉันออกจากการใช้งานตัวเขียนเพียงอย่างเดียวและมันก็ยังใช้ได้

public Response getSkuData()
...
return Response.status(Response.Status.FORBIDDEN).entity(dfCoreResultListDTO).build();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.