0

I use spring, spring boot and spring-rest controller and i begin with error.

My controller

@RequestMapping(value = "/members/{memberId}/creditcard", method = RequestMethod.POST)
 public void saveCreditCard(@PathVariable("memberId") Long memberId, @RequestBody CreditCardInfoDto creditCardInfo) {
     try {
         paymentService.saveCreditCard(creditCardInfo);
     } catch(Exception e) {}
 }

In my service,

 @Override
 public Long saveCreditCard(CreditCardInfoDto creditCardInfo) throws Exception {
     Member member = null;

     if (creditCardInfo.getMemberId() != null) {
         member = memberRepository.findOne(creditCardInfo.getMemberId());
     }

     List < PaymentGatewayConfig > paymentGateways = paymentGatewayConfigRepository.findAll();

     if (paymentGateways != null && !paymentGateways.isEmpty()) {

         PaymentGatewayConfig paymentGateway = paymentGateways.get(0);
         String paymentGatewayKey = null;

         if (member != null) {
             paymentGatewayKey = member.getPaymentGatewayKey();
         }

         ResolveData resdata = null;
         ResolverReceipt resreceipt = null;

         try {

             if (paymentGatewayKey == null) {

                 ResAddCC resAddCC = new ResAddCC(creditCardInfo.getCreditCard(), creditCardInfo.getDateExpiration(), "7");

                 //resAddCC.setAvsInfo(avsCheck);
                 ResolverHttpsPostRequest mpgReq = new ResolverHttpsPostRequest(paymentGateway.getHost(), paymentGateway.getStoreId(), paymentGateway.getApiToken(), resAddCC);
                 resreceipt = mpgReq.getResolverReceipt();
                 resdata = resreceipt.getResolveData();

                 member.setPaymentGatewayKey(resreceipt.getDataKey());
             } else {

                 ResUpdateCC resUpdateCC = new ResUpdateCC(member.getPaymentGatewayKey());
                 resUpdateCC.setPan(creditCardInfo.getCreditCard());
                 resUpdateCC.setExpdate(creditCardInfo.getDateExpiration());

                 ResolverHttpsPostRequest mpgReq = new ResolverHttpsPostRequest(paymentGateway.getHost(), paymentGateway.getStoreId(), paymentGateway.getApiToken(), resUpdateCC);

                 resreceipt = mpgReq.getResolverReceipt();
                 resdata = resreceipt.getResolveData();
             }
         } catch (Exception e) {
             log.error("saveCreditCard: " + member.getMemberId() + "ResolverHttpsPostRequest error");
         }

         if (resdata != null) {
             //success
             if (Boolean.valueOf(resreceipt.getComplete()) && !Boolean.valueOf(resreceipt.getTimedOut())) {
                 member.setPaymentGatewayKey(resreceipt.getDataKey());
                 memberRepository.save(member);
                 Operation operation = createOperation(resreceipt);
                 operationRepository.save(operation);
             } else {
                 Operation operation = createOperation(resreceipt);
                 operationRepository.save(operation);
                 //payment refused
                 log.error("saveCreditCard: " + member.getMemberId() + " payment refused error code: " + resreceipt.getResponseCode());
                 throw new Exception("Payment refused");
             }
         }
     }
     return 1 l;
 }

So in the service, error can happen when a call is done to ResolverHttpsPostRequest

I thow an exception if the payment it's refused.

It is the good way to manage error?

how to send these error to the client (web)?

robert trudel
  • 5,283
  • 17
  • 72
  • 124
  • If refusing the payment is a thing that can happen normally, then it's probably not an exception. You'll need to return a 4xx error of some type since it's a client-side issue. Maybe 400 or 402. – Paul Hicks May 29 '16 at 22:01
  • so you saveCreditCard should return true or false... to specified if the payment have been passed or not? – robert trudel May 30 '16 at 00:28
  • That's an implementation detail. Maybe saveCreditCard works as-is but the rest service wrapper catches the exception and turns it into an error response. Or maybe saveCreditCard continues to return a Long, but also updates a passed-in ValidationResults object or similar, as is done in Spring. The important bit for you is that you need to generate and return a valid HTTP response, with a suitable HTTP status code. – Paul Hicks May 30 '16 at 00:32
  • is it possible to get an error in a repository? I don't know how spring manage this. are there an standard way to do it with spring rest controller? if i got an error in my service, my controller will send it to the client? – robert trudel Jun 02 '16 at 01:44
  • Do you mean, might one of the method calls you make (like `memberRepository.findOne`) throw an exception? Absolutely. And this is the sort of thing that causes a 500 error. You can test it in many ways: I recommend judicial use of [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) and fakes (mocks, stubs, dummies or similar). – Paul Hicks Jun 02 '16 at 01:47
  • ok thank @PaulHicks. Actually, if i got an error in my service like an exception... because i catch it in the controller... my client will not get it? – robert trudel Jun 02 '16 at 01:50
  • Yes, it won't. You need to catch the exception _then construct and send a suitable error response_. Most rest and WS libraries will do this for you, or you can do it yourself, it's not hard. Just need to set the response code to some number between 500 and 599, rather than 200-299. See [this list of status codes](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes). – Paul Hicks Jun 02 '16 at 01:52
  • so in the service layer, we must not catch exception if we want to send it to client. What to do suggest? using response entity, @ControllerAdvice... – robert trudel Jun 02 '16 at 02:19
  • Why not? It's not the service layer's job to send a response. Let it throw exceptions. It's the API controller's job to decide how to build responses. It decides what to do with exceptions. – Paul Hicks Jun 02 '16 at 02:31
  • so in the method, you will do a try catch and you add throws to the signature? – robert trudel Jun 08 '16 at 00:11
  • Yes that would one way of doing it. Most (all?) libraries that provide this sort of functionality use unchecked exceptions, so you don't have to change the signature. Just catch in the controller and pass the request and exception to a response-generating method. Or something like that. – Paul Hicks Jun 08 '16 at 00:24

1 Answers1

1

You may use org.springframework.http.ResponseEntity. jHipster is a good example project to see how to implement it. They use request header to send some service results like success, fail, warning etc.

Here is a part of code from jHipster sample project

@RequestMapping(value = "/operations",
        method = RequestMethod.POST,
        produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Operation> createOperation(@Valid @RequestBody Operation operation) throws URISyntaxException {
        log.debug("REST request to save Operation : {}", operation);
        if (operation.getId() != null) {
            return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("operation", "idexists", "A new operation cannot already have an ID")).body(null);
        }
        Operation result = operationRepository.save(operation);
        return ResponseEntity.created(new URI("/api/operations/" + result.getId()))
            .headers(HeaderUtil.createEntityCreationAlert("operation", result.getId().toString()))
            .body(result);
    }
Ismail Sahin
  • 2,640
  • 5
  • 31
  • 58