Mercurial > hg > orthanc
comparison OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp @ 1569:27774f6f84e4
improved error handling in http answers
author | jodogne |
---|---|
date | Sun, 23 Aug 2015 11:01:15 +0200 |
parents | 3be6eb3757c8 |
children | 2bd2c280f9b5 |
comparison
equal
deleted
inserted
replaced
1568:818ae9bc493a | 1569:27774f6f84e4 |
---|---|
312 **/ | 312 **/ |
313 | 313 |
314 std::string modifiedInstance; | 314 std::string modifiedInstance; |
315 if (context.Store(modifiedInstance, toStore) != StoreStatus_Success) | 315 if (context.Store(modifiedInstance, toStore) != StoreStatus_Success) |
316 { | 316 { |
317 LOG(ERROR) << "Error while storing a modified instance " << *it; | 317 throw OrthancException("Error while storing a modified instance " + *it); |
318 return; | |
319 } | 318 } |
320 | 319 |
321 // Sanity checks in debug mode | 320 // Sanity checks in debug mode |
322 assert(modifiedInstance == modifiedHasher.HashInstance()); | 321 assert(modifiedInstance == modifiedHasher.HashInstance()); |
323 | 322 |
429 changeType, resourceType, call); | 428 changeType, resourceType, call); |
430 } | 429 } |
431 } | 430 } |
432 | 431 |
433 | 432 |
434 static bool StoreCreatedInstance(std::string& id /* out */, | 433 static void StoreCreatedInstance(std::string& id /* out */, |
435 ServerContext& context, | 434 ServerContext& context, |
436 ParsedDicomFile& dicom) | 435 ParsedDicomFile& dicom) |
437 { | 436 { |
438 DicomInstanceToStore toStore; | 437 DicomInstanceToStore toStore; |
439 toStore.SetParsedDicomFile(dicom); | 438 toStore.SetParsedDicomFile(dicom); |
440 | 439 |
441 StoreStatus status = context.Store(id, toStore); | 440 StoreStatus status = context.Store(id, toStore); |
442 | 441 |
443 if (status == StoreStatus_Failure) | 442 if (status == StoreStatus_Failure) |
444 { | 443 { |
445 LOG(ERROR) << "Error while storing a manually-created instance"; | 444 throw OrthancException("Error while storing a manually-created instance"); |
446 return false; | 445 } |
447 } | 446 } |
448 else | 447 |
449 { | 448 |
450 return true; | 449 static void CreateDicomV1(ParsedDicomFile& dicom, |
451 } | |
452 } | |
453 | |
454 | |
455 static bool CreateDicomV1(ParsedDicomFile& dicom, | |
456 const Json::Value& request) | 450 const Json::Value& request) |
457 { | 451 { |
458 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World"}' | 452 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World"}' |
459 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World","PixelData":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII="}' | 453 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World","PixelData":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII="}' |
460 | 454 |
465 for (size_t i = 0; i < members.size(); i++) | 459 for (size_t i = 0; i < members.size(); i++) |
466 { | 460 { |
467 const std::string& name = members[i]; | 461 const std::string& name = members[i]; |
468 if (request[name].type() != Json::stringValue) | 462 if (request[name].type() != Json::stringValue) |
469 { | 463 { |
470 LOG(ERROR) << "Only string values are supported when creating DICOM instances"; | 464 throw OrthancException("Only string values are supported when creating DICOM instances"); |
471 return false; | |
472 } | 465 } |
473 | 466 |
474 std::string value = request[name].asString(); | 467 std::string value = request[name].asString(); |
475 | 468 |
476 DicomTag tag = FromDcmtkBridge::ParseTag(name); | 469 DicomTag tag = FromDcmtkBridge::ParseTag(name); |
481 else | 474 else |
482 { | 475 { |
483 dicom.Replace(tag, value); | 476 dicom.Replace(tag, value); |
484 } | 477 } |
485 } | 478 } |
486 | 479 } |
487 return true; | 480 |
488 } | 481 |
489 | 482 static void InjectTags(ParsedDicomFile& dicom, |
490 | |
491 static bool InjectTags(ParsedDicomFile& dicom, | |
492 const Json::Value& tags) | 483 const Json::Value& tags) |
493 { | 484 { |
494 if (tags.type() != Json::objectValue) | 485 if (tags.type() != Json::objectValue) |
495 { | 486 { |
496 LOG(ERROR) << "Bad syntax to specify the tags"; | 487 throw OrthancException("Bad syntax to specify the tags"); |
497 return false; | |
498 } | 488 } |
499 | 489 |
500 // Inject the user-specified tags | 490 // Inject the user-specified tags |
501 Json::Value::Members members = tags.getMemberNames(); | 491 Json::Value::Members members = tags.getMemberNames(); |
502 for (size_t i = 0; i < members.size(); i++) | 492 for (size_t i = 0; i < members.size(); i++) |
503 { | 493 { |
504 const std::string& name = members[i]; | 494 const std::string& name = members[i]; |
505 if (tags[name].type() != Json::stringValue) | 495 if (tags[name].type() != Json::stringValue) |
506 { | 496 { |
507 LOG(ERROR) << "Only string values are supported when creating DICOM instances"; | 497 throw OrthancException("Only string values are supported when creating DICOM instances"); |
508 return false; | |
509 } | 498 } |
510 | 499 |
511 std::string value = tags[name].asString(); | 500 std::string value = tags[name].asString(); |
512 | 501 |
513 DicomTag tag = FromDcmtkBridge::ParseTag(name); | 502 DicomTag tag = FromDcmtkBridge::ParseTag(name); |
514 if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET) | 503 if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET) |
515 { | 504 { |
516 if (tag != DICOM_TAG_PATIENT_ID && | 505 if (tag != DICOM_TAG_PATIENT_ID && |
517 dicom.HasTag(tag)) | 506 dicom.HasTag(tag)) |
518 { | 507 { |
519 LOG(ERROR) << "Trying to override a value inherited from a parent module"; | 508 throw OrthancException("Trying to override a value inherited from a parent module"); |
520 return false; | |
521 } | 509 } |
522 | 510 |
523 if (tag == DICOM_TAG_PIXEL_DATA) | 511 if (tag == DICOM_TAG_PIXEL_DATA) |
524 { | 512 { |
525 LOG(ERROR) << "Use \"Content\" to inject an image into a new DICOM instance"; | 513 throw OrthancException("Use \"Content\" to inject an image into a new DICOM instance"); |
526 return false; | |
527 } | 514 } |
528 else | 515 else |
529 { | 516 { |
530 dicom.Replace(tag, Toolbox::ConvertFromUtf8(value, dicom.GetEncoding())); | 517 dicom.Replace(tag, Toolbox::ConvertFromUtf8(value, dicom.GetEncoding())); |
531 } | 518 } |
532 } | 519 } |
533 } | 520 } |
534 | |
535 return true; | |
536 } | 521 } |
537 | 522 |
538 | 523 |
539 static void CreateSeries(RestApiPostCall& call, | 524 static void CreateSeries(RestApiPostCall& call, |
540 ParsedDicomFile& base /* in */, | 525 ParsedDicomFile& base /* in */, |
547 base.Replace(DICOM_TAG_IMAGES_IN_ACQUISITION, boost::lexical_cast<std::string>(content.size())); | 532 base.Replace(DICOM_TAG_IMAGES_IN_ACQUISITION, boost::lexical_cast<std::string>(content.size())); |
548 base.Replace(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, "1"); | 533 base.Replace(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, "1"); |
549 | 534 |
550 std::string someInstance; | 535 std::string someInstance; |
551 | 536 |
552 bool ok = true; | |
553 | |
554 try | 537 try |
555 { | 538 { |
556 for (Json::ArrayIndex i = 0; ok && i < content.size(); i++) | 539 for (Json::ArrayIndex i = 0; i < content.size(); i++) |
557 { | 540 { |
558 std::auto_ptr<ParsedDicomFile> dicom(base.Clone()); | 541 std::auto_ptr<ParsedDicomFile> dicom(base.Clone()); |
559 const Json::Value* payload = NULL; | 542 const Json::Value* payload = NULL; |
560 | 543 |
561 if (content[i].type() == Json::stringValue) | 544 if (content[i].type() == Json::stringValue) |
564 } | 547 } |
565 else if (content[i].type() == Json::objectValue) | 548 else if (content[i].type() == Json::objectValue) |
566 { | 549 { |
567 if (!content[i].isMember("Content")) | 550 if (!content[i].isMember("Content")) |
568 { | 551 { |
569 LOG(ERROR) << "No payload is present for one instance in the series"; | 552 throw OrthancException("No payload is present for one instance in the series"); |
570 ok = false; | |
571 break; | |
572 } | 553 } |
573 | 554 |
574 payload = &content[i]["Content"]; | 555 payload = &content[i]["Content"]; |
575 | 556 |
576 if (content[i].isMember("Tags") && | 557 if (content[i].isMember("Tags")) |
577 !InjectTags(*dicom, content[i]["Tags"])) | 558 { |
578 { | 559 InjectTags(*dicom, content[i]["Tags"]); |
579 ok = false; | 560 } |
580 break; | |
581 } | |
582 } | 561 } |
583 | 562 |
584 if (payload == NULL || | 563 if (payload == NULL || |
585 payload->type() != Json::stringValue) | 564 payload->type() != Json::stringValue) |
586 { | 565 { |
587 LOG(ERROR) << "The payload of the DICOM instance must be specified according to Data URI scheme"; | 566 throw OrthancException("The payload of the DICOM instance must be specified according to Data URI scheme"); |
588 ok = false; | |
589 break; | |
590 } | 567 } |
591 | 568 |
592 dicom->EmbedContent(payload->asString()); | 569 dicom->EmbedContent(payload->asString()); |
593 dicom->Replace(DICOM_TAG_INSTANCE_NUMBER, boost::lexical_cast<std::string>(i + 1)); | 570 dicom->Replace(DICOM_TAG_INSTANCE_NUMBER, boost::lexical_cast<std::string>(i + 1)); |
594 dicom->Replace(DICOM_TAG_IMAGE_INDEX, boost::lexical_cast<std::string>(i + 1)); | 571 dicom->Replace(DICOM_TAG_IMAGE_INDEX, boost::lexical_cast<std::string>(i + 1)); |
595 | 572 |
596 if (!StoreCreatedInstance(someInstance, context, *dicom)) | 573 StoreCreatedInstance(someInstance, context, *dicom); |
597 { | 574 } |
598 LOG(ERROR) << "Error while creating the series"; | 575 } |
599 ok = false; | 576 catch (OrthancException& e) |
600 break; | 577 { |
601 } | 578 // Error: Remove the newly-created series |
602 } | 579 |
603 } | 580 std::string series; |
604 catch (OrthancException&) | 581 if (context.GetIndex().LookupParent(series, someInstance)) |
605 { | 582 { |
606 ok = false; | 583 Json::Value dummy; |
584 context.GetIndex().DeleteResource(dummy, series, ResourceType_Series); | |
585 } | |
586 | |
587 throw e; | |
607 } | 588 } |
608 | 589 |
609 std::string series; | 590 std::string series; |
610 if (context.GetIndex().LookupParent(series, someInstance)) | 591 if (context.GetIndex().LookupParent(series, someInstance)) |
611 { | 592 { |
612 if (ok) | 593 OrthancRestApi::GetApi(call).AnswerStoredResource(call, series, ResourceType_Series, StoreStatus_Success); |
613 { | |
614 OrthancRestApi::GetApi(call).AnswerStoredResource(call, series, ResourceType_Series, StoreStatus_Success); | |
615 } | |
616 else | |
617 { | |
618 // Error: Remove the newly-created series | |
619 Json::Value dummy; | |
620 context.GetIndex().DeleteResource(dummy, series, ResourceType_Series); | |
621 } | |
622 } | 594 } |
623 } | 595 } |
624 | 596 |
625 | 597 |
626 static void CreateDicomV2(RestApiPostCall& call, | 598 static void CreateDicomV2(RestApiPostCall& call, |
630 ServerContext& context = OrthancRestApi::GetContext(call); | 602 ServerContext& context = OrthancRestApi::GetContext(call); |
631 | 603 |
632 if (!request.isMember("Tags") || | 604 if (!request.isMember("Tags") || |
633 request["Tags"].type() != Json::objectValue) | 605 request["Tags"].type() != Json::objectValue) |
634 { | 606 { |
635 return; | 607 throw OrthancException(ErrorCode_BadRequest); |
636 } | 608 } |
637 | 609 |
638 ParsedDicomFile dicom; | 610 ParsedDicomFile dicom; |
639 | 611 |
640 { | 612 { |
643 if (request["Tags"].isMember("SpecificCharacterSet")) | 615 if (request["Tags"].isMember("SpecificCharacterSet")) |
644 { | 616 { |
645 const char* tmp = request["Tags"]["SpecificCharacterSet"].asCString(); | 617 const char* tmp = request["Tags"]["SpecificCharacterSet"].asCString(); |
646 if (!GetDicomEncoding(encoding, tmp)) | 618 if (!GetDicomEncoding(encoding, tmp)) |
647 { | 619 { |
648 LOG(ERROR) << "Unknown specific character set: " << tmp; | 620 throw OrthancException("Unknown specific character set: " + std::string(tmp)); |
649 return; | |
650 } | 621 } |
651 } | 622 } |
652 else | 623 else |
653 { | 624 { |
654 std::string tmp = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1"); | 625 std::string tmp = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1"); |
664 { | 635 { |
665 // Locate the parent tags | 636 // Locate the parent tags |
666 std::string parent = request["Parent"].asString(); | 637 std::string parent = request["Parent"].asString(); |
667 if (!context.GetIndex().LookupResourceType(parentType, parent)) | 638 if (!context.GetIndex().LookupResourceType(parentType, parent)) |
668 { | 639 { |
669 LOG(ERROR) << "Trying to attach a new DICOM instance to an inexistent resource: " << parent; | 640 throw OrthancException("Trying to attach a new DICOM instance to an inexistent resource: " + parent); |
670 return; | |
671 } | 641 } |
672 | 642 |
673 if (parentType == ResourceType_Instance) | 643 if (parentType == ResourceType_Instance) |
674 { | 644 { |
675 LOG(ERROR) << "Trying to attach a new DICOM instance to an instance (must be a series, study or patient): " << parent; | 645 throw OrthancException("Trying to attach a new DICOM instance to an instance (must be a series, study or patient): " + parent); |
676 return; | |
677 } | 646 } |
678 | 647 |
679 // Select one existing child instance of the parent resource, to | 648 // Select one existing child instance of the parent resource, to |
680 // retrieve all its tags | 649 // retrieve all its tags |
681 Json::Value siblingTags; | 650 Json::Value siblingTags; |
684 // Retrieve all the instances of the parent resource | 653 // Retrieve all the instances of the parent resource |
685 std::list<std::string> siblingInstances; | 654 std::list<std::string> siblingInstances; |
686 context.GetIndex().GetChildInstances(siblingInstances, parent); | 655 context.GetIndex().GetChildInstances(siblingInstances, parent); |
687 | 656 |
688 if (siblingInstances.empty()) | 657 if (siblingInstances.empty()) |
689 { | 658 { |
690 return; // Error: No instance (should never happen) | 659 // Error: No instance (should never happen) |
660 throw OrthancException(ErrorCode_InternalError); | |
691 } | 661 } |
692 | 662 |
693 context.ReadJson(siblingTags, siblingInstances.front()); | 663 context.ReadJson(siblingTags, siblingInstances.front()); |
694 } | 664 } |
695 | 665 |
703 Encoding encoding; | 673 Encoding encoding; |
704 if (!siblingTags[SPECIFIC_CHARACTER_SET].isMember("Value") || | 674 if (!siblingTags[SPECIFIC_CHARACTER_SET].isMember("Value") || |
705 siblingTags[SPECIFIC_CHARACTER_SET]["Value"].type() != Json::stringValue || | 675 siblingTags[SPECIFIC_CHARACTER_SET]["Value"].type() != Json::stringValue || |
706 !GetDicomEncoding(encoding, siblingTags[SPECIFIC_CHARACTER_SET]["Value"].asCString())) | 676 !GetDicomEncoding(encoding, siblingTags[SPECIFIC_CHARACTER_SET]["Value"].asCString())) |
707 { | 677 { |
708 LOG(ERROR) << "Unable to get the encoding of the parent resource"; | 678 throw OrthancException("Unable to get the encoding of the parent resource"); |
709 return; | |
710 } | 679 } |
711 | 680 |
712 dicom.SetEncoding(encoding); | 681 dicom.SetEncoding(encoding); |
713 } | 682 } |
714 } | 683 } |
730 | 699 |
731 // Go up | 700 // Go up |
732 std::string tmp; | 701 std::string tmp; |
733 if (!context.GetIndex().LookupParent(tmp, parent)) | 702 if (!context.GetIndex().LookupParent(tmp, parent)) |
734 { | 703 { |
735 return; | 704 throw OrthancException(ErrorCode_InternalError); |
736 } | 705 } |
737 | 706 |
738 parent = tmp; | 707 parent = tmp; |
739 type = GetParentResourceType(type); | 708 type = GetParentResourceType(type); |
740 } | 709 } |
784 dicom.Replace(DICOM_TAG_STUDY_DATE, date); | 753 dicom.Replace(DICOM_TAG_STUDY_DATE, date); |
785 dicom.Replace(DICOM_TAG_STUDY_TIME, time); | 754 dicom.Replace(DICOM_TAG_STUDY_TIME, time); |
786 } | 755 } |
787 | 756 |
788 | 757 |
789 if (!InjectTags(dicom, request["Tags"])) | 758 InjectTags(dicom, request["Tags"]); |
790 { | |
791 return; // Error | |
792 } | |
793 | 759 |
794 | 760 |
795 // Inject the content (either an image, or a PDF file) | 761 // Inject the content (either an image, or a PDF file) |
796 if (request.isMember("Content")) | 762 if (request.isMember("Content")) |
797 { | 763 { |
811 return; | 777 return; |
812 } | 778 } |
813 } | 779 } |
814 else | 780 else |
815 { | 781 { |
816 LOG(ERROR) << "The payload of the DICOM instance must be specified according to Data URI scheme"; | 782 throw OrthancException("The payload of the DICOM instance must be specified according to Data URI scheme"); |
817 return; | 783 return; |
818 } | 784 } |
819 } | 785 } |
820 | 786 |
821 std::string id; | 787 std::string id; |
822 if (StoreCreatedInstance(id, context, dicom)) | 788 StoreCreatedInstance(id, context, dicom); |
823 { | 789 OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success); |
824 OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success); | |
825 } | |
826 | 790 |
827 return; | 791 return; |
828 } | 792 } |
829 | 793 |
830 | 794 |
831 static void CreateDicom(RestApiPostCall& call) | 795 static void CreateDicom(RestApiPostCall& call) |
832 { | 796 { |
833 Json::Value request; | 797 Json::Value request; |
834 if (call.ParseJsonRequest(request) && | 798 if (!call.ParseJsonRequest(request) || |
835 request.isObject()) | 799 !request.isObject()) |
836 { | 800 { |
837 if (request.isMember("Tags")) | 801 throw OrthancException(ErrorCode_BadRequest); |
838 { | 802 } |
839 CreateDicomV2(call, request); | 803 |
840 } | 804 if (request.isMember("Tags")) |
841 else | 805 { |
842 { | 806 CreateDicomV2(call, request); |
843 // Compatibility with Orthanc <= 0.9.3 | 807 } |
844 ServerContext& context = OrthancRestApi::GetContext(call); | 808 else |
845 ParsedDicomFile dicom; | 809 { |
846 if (CreateDicomV1(dicom, request)) | 810 // Compatibility with Orthanc <= 0.9.3 |
847 { | 811 ServerContext& context = OrthancRestApi::GetContext(call); |
848 std::string id; | 812 ParsedDicomFile dicom; |
849 if (StoreCreatedInstance(id, context, dicom)) | 813 CreateDicomV1(dicom, request); |
850 { | 814 |
851 OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success); | 815 std::string id; |
852 } | 816 StoreCreatedInstance(id, context, dicom); |
853 } | 817 OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success); |
854 } | 818 } |
855 } | |
856 } | 819 } |
857 | 820 |
858 | 821 |
859 void OrthancRestApi::RegisterAnonymizeModify() | 822 void OrthancRestApi::RegisterAnonymizeModify() |
860 { | 823 { |