I am using React Js in my client-side and .Net Core 3.0 Web API on the server-side. I have one API method called CreateAccount and the return type is IActionResult. Now if I do validate with any one of the model property then I have to send or return the validation message along with empty model data. I am new to API and tried like below but could not send the string as a result type.
API method,
[AllowAnonymous]
[HttpPost("createaccount")]
public async Task<IActionResult> CreateAccount([FromBody]Users user)
{
try
{
if (ModelState.IsValid)
{
if (user == null)
return BadRequest(new { message = "Data is empty" });
if(user.UserType!="Admin")
{
return new ValidationResult("Only Admin can create new account");
}
return Ok(await _userService.CreateAnUserAccount(user));
}
}
catch(Exception e)
{
throw new ArgumentException(e.Message);
}
return ValidationProblem();
}
I do not know the proper .Net Core API coding part, could anyone please help me to resolve this issue?
You could return an ObjectResult with a StatusCode other than StatusCodes.Status200OK and a serialized object that contains whatever information you want to return to the client, e.g.:
return new ObjectResult(new YourApiError() { Message = "message.." })
{
StatusCode = StatusCodes.Status405MethodNotAllowed
};
I have an API endpoint that returns file as attachment. for example if I access www.myfileservice.com/api/files/download/123 I could download the file directly. My requirement is to use this endpoint in another ASP.Net MVC project. So if the user hits www.mymvcapplication.com/File/DownloadDocument/123 it should also download the same file. Internally the action method should call the file service API and return the result as it is. This is the code I am using:
FileController.cs:
public HttpResponseMessage DownloadDocument(int Id)
{
return new DocumentClient().DownloadDocument(Id);
}
DocumentClient.cs:
public class DocumentClient
{
private string documentServiceURL = string.Empty;
private static string downloadDocumentUri = "api/files/download/";
protected HttpClient documentClient = null;
public DocumentClient()
{
documentServiceURL = "www.myfileservice.com";
documentClient = new HttpClient();
documentClient.BaseAddress = new Uri(documentServiceURL);
}
public HttpResponseMessage DownloadDocument(int Id)
{
return documentClient.GetAsync(String.Format("{0}/{1}", downloadDocumentUri, Id)).Result;
}
}
The code above is not giving any error but only printing the response in browser window(Content-Length, Content-Disposition etc). I need to download the file instead.
I think the best is to return a FileResult from your controller:
public FileResult DownloadDocument(int Id)
{
var document = new DocumentClient().DownloadDocument(Id);
//do the transformation here
//...
//I don't know what is your file's extension, please replace "application/zip" if
//needed
return File(finalResult, "application/zip", fileName);
}
I have a web service method, which looks like this:
[HttpGet("{id}")]
public ActionResult<byte[]> Get(Guid id)
{
var files = Directory.GetFiles(#"Pictures\");
foreach (var file in files)
{
if (file.Contains(id.ToString()))
{
return System.IO.File.ReadAllBytes(file);
}
}
return null;
}
Here is the client code, which is definitely working i.e. it is calling the web service and the web service is returning the image:
var response2 = await client.GetAsync("http://localhost:59999/api/Images/5c60f693-bef5-e011-a485-80ee7300c692");
byte[] image2 = await response2.Content.ReadAsByteArrayAsync(); //https://stackoverflow.com/questions/39190018/how-to-get-object-using-httpclient-with-response-ok-in-web-api
System.IO.File.WriteAllBytes("image.jpg", image2);
When I try to open image.jpg in Paint; it says it is an invalid file. What is the problem?
If you want to return file do not return byte[] from action because it gets base64 encoded. You can decode base64 string on client or better would be using File method in action
[HttpGet("{id}")]
public ActionResult Get(Guid id)
{
var files = Directory.GetFiles(#"Pictures\");
foreach (var file in files)
{
if (file.Contains(id.ToString()))
{
return File(System.IO.File.ReadAllBytes(file), "image/jpeg");
}
}
return null;
}
Problem
I want to return a file in my ASP.Net Web API Controller, but all my approaches return the HttpResponseMessage as JSON.
Code so far
public async Task<HttpResponseMessage> DownloadAsync(string id)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent({{__insert_stream_here__}});
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return response;
}
When I call this endpoint in my browser, the Web API returns the HttpResponseMessage as JSON with the HTTP Content Header set to application/json.
If this is ASP.net-Core then you are mixing web API versions. Have the action return a derived IActionResult because in your current code the framework is treating HttpResponseMessage as a model.
[Route("api/[controller]")]
public class DownloadController : Controller {
//GET api/download/12345abc
[HttpGet("{id}")]
public async Task<IActionResult> Download(string id) {
Stream stream = await {{__get_stream_based_on_id_here__}}
if(stream == null)
return NotFound(); // returns a NotFoundResult with Status404NotFound response.
return File(stream, "application/octet-stream", "{{filename.ext}}"); // returns a FileStreamResult
}
}
Note:
The framework will dispose of the stream used in this case when the response is completed. If a using statement is used, the stream will be disposed before the response has been sent and result in an exception or corrupt response.
You can return FileResult with this methods:
1: Return FileStreamResult
[HttpGet("get-file-stream/{id}"]
public async Task<FileStreamResult> DownloadAsync(string id)
{
var fileName="myfileName.txt";
var mimeType="application/....";
Stream stream = await GetFileStreamById(id);
return new FileStreamResult(stream, mimeType)
{
FileDownloadName = fileName
};
}
2: Return FileContentResult
[HttpGet("get-file-content/{id}"]
public async Task<FileContentResult> DownloadAsync(string id)
{
var fileName="myfileName.txt";
var mimeType="application/....";
byte[] fileBytes = await GetFileBytesById(id);
return new FileContentResult(fileBytes, mimeType)
{
FileDownloadName = fileName
};
}
Here is a simplistic example of streaming a file:
using System.IO;
using Microsoft.AspNetCore.Mvc;
[HttpGet("{id}")]
public async Task<FileStreamResult> Download(int id)
{
var path = "<Get the file path using the ID>";
var stream = File.OpenRead(path);
return new FileStreamResult(stream, "application/octet-stream");
}
Note:
Be sure to use FileStreamResult from Microsoft.AspNetCore.Mvc and not from System.Web.Mvc.
ASP.NET 5 WEB API & Angular 12
You can return a FileContentResult object (Blob) from the server. It'll not get automatically downloaded. You may create an anchor tag in your front-end app programmatically and set the href property to an object URL created from the Blob by the method below. Now clicking on the anchor will download the file. You can set a file name by setting the 'download' attribute to the anchor as well.
downloadFile(path: string): Observable<any> {
return this._httpClient.post(`${environment.ApiRoot}/accountVerification/downloadFile`, { path: path }, {
observe: 'response',
responseType: 'blob'
});
}
saveFile(path: string, fileName: string): void {
this._accountApprovalsService.downloadFile(path).pipe(
take(1)
).subscribe((resp) => {
let downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(resp.body);
downloadLink.setAttribute('download', fileName);
document.body.appendChild(downloadLink);
downloadLink.click();
downloadLink.remove();
});
}
Backend
[HttpPost]
[Authorize(Roles = "SystemAdmin, SystemUser")]
public async Task<IActionResult> DownloadFile(FilePath model)
{
if (ModelState.IsValid)
{
try
{
var fileName = System.IO.Path.GetFileName(model.Path);
var content = await System.IO.File.ReadAllBytesAsync(model.Path);
new FileExtensionContentTypeProvider()
.TryGetContentType(fileName, out string contentType);
return File(content, contentType, fileName);
}
catch
{
return BadRequest();
}
}
return BadRequest();
}
Following is the basic example of returning file (e.g Image file) in .NET Core Web API:
<img src="#Url.Action("RenderImage", new { id = id})" alt="No Image found" />
Below is the code for returning File from controller to view. Following is Action method which will return file:
[Route("api/[controller]")]
public class DownloadController : Controller
{
//GET api/download/123
[HttpGet]
public async Task<IActionResult> RenderImage(string userId)
{
//get Image file using _fileservice from db
var result = await _fileService.getFile(userId);
if (result.byteStream == null)
return NotFound();
return File(result.byteStream, result.ContentType, result.FileName);
}
}
Note:
Our file should be first converted into byte[] and then saved in database as varbinary(max) in order to retrieve.
FileStreamResult works for me. and File is not an IActionResult. I don't know how it can work.
add
builder.Services.AddSingleton();
in
Program.cs
[HttpGet("{fileId}")]
public ActionResult GetFile(string fileId)
{
string pathToFile = "test.rar";
if (!System.IO.File.Exists(pathToFile))
{
return NotFound();
}
if(!_fileExtensionContentTypeProvider.TryGetContentType(pathToFile,
out var ContentType))
{
ContentType = "application/octet-stream";
}
var byets=System.IO.File.ReadAllBytes(pathToFile);
return File(byets, ContentType, Path.GetFileName(pathToFile));
}
}
I'm wonder how can I build full path to the action within my signalR hub. I have code in my hub:
public string GetUpdateUrl(string identifier)
{
var helper = new System.Web.Mvc.UrlHelper(HttpContext.Current.Request.RequestContext);
var result = String.Empty;
try
{
result = helper.Action("Download", "Agent", identifier);
}
catch (Exception e)
{
var t = e;
}
return result;
}
What I want is just to return full url (like example.com/Conteroller/Action?identifier=some_code) to the action.
And here Download action
public ActionResult Download(string identifier)
{
//return download
}
But I'm getting error.
Response is not available in this context.
Since your SignalR service is self hosted by owin (isn't it?), you shall try to check this and this answers.