/// <summary> /// CreateDocument returns an error result when an error occurs; null otherwise. If CreateDocument /// return null, then the out parameter doc is guaranteed to be valid. /// </summary> /// <returns>An error result or null. A null result indicates that the document was created successfully.</returns> IHttpActionResult CreateDocument(List <Error> errors, Application app, DocumentCreateAttributes attr, out Document doc) { doc = null; var docType = app.Core.DocumentTypes.Find(attr.DocumentType); if (docType == null) { errors.Add(BadRequestError($"The DocumentType '{attr.DocumentType}' could not be found.")); } var fileType = app.Core.FileTypes.Find(attr.FileType); if (fileType == null) { errors.Add(BadRequestError($"The FileType '{attr.FileType}' could not be found.")); } var pageData = app.Core.Storage.CreatePageData(attr.Stream, attr.FileExtension); if (pageData == null) { errors.Add(BadRequestError($"Unable to create page data for '{attr.FileExtension}'.")); } var props = app.Core.Storage.CreateStoreNewDocumentProperties(docType, fileType); if (props == null) { return(BadRequestResult($"Unable to create document properties for '{attr.DocumentType}' and '{attr.FileType}'.")); } if (errors.Any()) { return(BadRequestResult(errors)); } props.AddKeyword(Global.Config.DocIndexKeyName, attr.IndexKey); props.DocumentDate = DateTime.Now; props.Comment = attr.Comment; props.ExpandKeysets = false; if (attr.Keywords != null) { foreach (var kw in attr.Keywords) { props.AddKeyword(kw.Name, kw.Value); } } doc = app.Core.Storage.StoreNewDocument(pageData, props); if (doc == null) { return(BadRequestResult("Unable to create document.")); } return(null); }
public async Task <IHttpActionResult> Post() { // We only support multi-part post content. if (!Request.Content.IsMimeMultipartContent()) { return(BadRequestResult("Uploading a document requires a multi-part POST request.")); } Stream docStream = null; string docExtension = null; DocumentPostAttributes docAttr = null; // We bundle the bad requests in one response. var errors = new List <Error>(); try { var provider = await Request.Content.ReadAsMultipartAsync( new MultipartMemoryStreamProvider()); foreach (HttpContent content in provider.Contents) { // The Content-Disposition header is required. if (content.Headers.ContentDisposition == null) { return(BadRequestResult("A Content-Disposition header is required.")); } var dispoName = content.Headers.ContentDisposition.Name.Trim('"'); if (dispoName == "file") { // The content is a file. // Only allow one content file. if ((docStream != null) || !string.IsNullOrWhiteSpace(docExtension)) { return(BadRequestResult("More than one document content was included.")); } var fileName = content.Headers.ContentDisposition.FileName.Trim('"'); docStream = content.ReadAsStreamAsync().Result; docExtension = Path.GetExtension(fileName).TrimStart('.'); // Handle base64 file encoding. if (content.Headers.ContentEncoding.Any(x => x == "base64") || content.Headers.Any(x => x.Key == "Content-Transfer-Encoding" && x.Value.Any(v => v == "base64"))) { /* * We need to read the file content, bease64 decode it and set * docStream to be the decoded content. */ using (var reader = new StreamReader(docStream)) { var str = reader.ReadToEnd(); docStream = new MemoryStream(Convert.FromBase64String(str)); } } } else if (dispoName == "attributes") { // The content is JSON. // Only allow one JSON attributes. if (docAttr != null) { return(BadRequestResult("More than one document attributes was included.")); } docAttr = JObject .Parse(content.ReadAsStringAsync().Result) .ToObject <DocumentPostAttributes>(); } else { return(BadRequestResult($"Unexpected Content-Disposition header of name '{dispoName}'.")); } } } catch (JsonReaderException ex) { errors.Add(BadRequestError($"JSON parse error. {ex.Message}")); } catch (IOException ex) when(ex.Message.Contains("MIME multipart message is not complete")) { errors.Add(BadRequestError(ex.Message)); } catch (Exception ex) { return(InternalErrorResult(ex.Message)); } if ((docStream == null) || string.IsNullOrWhiteSpace(docExtension)) { errors.Add(BadRequestError("The required parameter 'file' is missing.")); } if (docAttr == null) { errors.Add(BadRequestError("The required parameter 'attributes' is missing.")); } if ((docAttr == null) || string.IsNullOrWhiteSpace(docAttr.IndexKey)) { errors.Add(BadRequestError("The required parameter 'IndexKey' is empty.")); } if ((docAttr == null) || string.IsNullOrWhiteSpace(docAttr.DocumentType)) { errors.Add(BadRequestError("The required parameter 'DocumentType' is empty.")); } // Check if there are any errors. If there are then return them. if (errors.Any()) { return(BadRequestResult(errors)); } return(TryHandleRequest(app => { // Validate that the doc type is actually valid. var docType = app.Core.DocumentTypes.Find(docAttr.DocumentType); if (docType == null) { errors.Add(BadRequestError($"The DocumentType '{docAttr.DocumentType}' could not be found.")); } var createAttr = new DocumentCreateAttributes { DocumentType = Global.Config.StagingDocType, Comment = docAttr.Comment, IndexKey = docAttr.IndexKey, Keywords = docAttr.Keywords, FileType = docAttr.FileType ?? docExtension, FileExtension = docExtension, Stream = docStream, }; var error = CreateDocument(errors, app, createAttr, out var doc); if (error != null) { return error; } // We must get the document ID from this thread and not the task thread since // the document could go out of scope before the task starts. var docId = doc.ID; Task.Run(() => { MoveDocumentToWorkflow(docId, docType); }); return DocumentResult(doc, docId.ToString(), docAttr.DocumentType); })); }