Can't call Web Api by Alamofire.upload multipartFormData - c#

I have an Windows WEB API with the following method:
public async Task<IHttpActionResult> SaveContract([FromBody] ModelDTO model)
{
string custName = model.CustomerName;
...
}
The Model I want looks like this:
public class ModelDTO
{
public int CustomerNumber { set; get; }
public string CustomerName { set; get; }
public string CustomerMail { set; get; }
public string imageDataBase64 { set; get; }
}
I want to call the API with my iOS App (Swift 4) with Alamofire 4.7.2
My dev server has a self-signed certificate. So I need to disable the evaluation.
let defaultManager: Alamofire.SessionManager = {
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"devserver": .disableEvaluation
]
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
return Alamofire.SessionManager(
configuration: configuration,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
}()
let webApi: String = "https://devserver:7208/api/KP/SaveContract"
let data = UIImageJPEGRepresentation(photoImageView.image!,1) //got the Data form an image view
var imgString: String = ""
imgString = data.base64EncodedString()
let Param = Parameters = [
"CustomerNumber": 1,
"CustomerName": "Test Name",
"CustomerMail": "test#test.com",
"imageDataBase64": imgString]
defaultManager.upload(
multipartFormData: { multipartFormData in
for (key, value) in contAsJsonParam {
multipartFormData.append("\(value)".data(using: .utf8)!, withName:key)
}
},
to: webApi,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
debugPrint(response)
//lbl sichtbar machen
}
case .failure(let encodingError):
print(encodingError)
}
})
Call the api without image with Alamofire.request works, but with image request, it dosen't work. (bad ssl error)
So I try the upload method, but upload dosen't work in any way (with or without image string)
If I call the Api with Alamofire.upload I got a system.net.http.unsupportedmediatypeexception
"No MediaTypeFormatter is available to read an object of type
'ModelDTO' from content with media type 'multipart/form-data'."
I try to make the upload class as json by at "headers: Content-Type:application/json"
but no effect.
I try to fix it by putting
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
in the WebApiConfig. Then i got an other error
I got an NullReferenceException in the api at the line "string custName = model.CustomerName;"

You can use this code. I have tested this code with multipart data. It's working fine for me.
let url = "https://devserver:7208/api/KP/SaveContract"
//imp data need to be dynamic
let parameters: NSMutableDictionary = ["userId":"1123",
"caption":"hello how are you",
"keyword":"First Post",
"askPrice":"12345",
"postScope":"1",
"commentStatus":"1",
"gender":"male",
"email":"asd#asd.com"
]
var dictionaryHeaders = [String : String]()
dictionaryHeaders = ["Content-Type": "application/x-www-form-urlencoded" ]
Alamofire.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as! String)
}
self.postImage = UIImage(named:"YOUR IMAGE NAME")
if let data = UIImageJPEGRepresentation(self.postImage,1) {
multipartFormData.append(data, withName: "postPic", fileName: "image.png", mimeType: "image/png")
}
}, usingThreshold: UInt64.init(), to: url, method: .post, headers: dictionaryHeaders ) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseJSON{ response in
print(response)
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
}
}

Related

HttpRequest.ReadFromJsonAsync does not throw error for JSON that does not match the model

I am testing out minimal-api's on .NET 6 and run into some strange behavior where the deserialization of the JSON content in the request never fails as long as it is a well-formed JSON document. The documentation for the method says it will throw an error if "the request's content-type is not a known JSON type", but it never does...? Am I missing something basic here?
I have a simple model class Message:
public record Message
{
public string Receiver { get; set; } = "";
public string Content { get; set; } = "";
}
I add an endpoint like this:
app.MapPost("/send", async (http) =>
{
var msg = await http.Request.ReadFromJsonAsync<Message>()
if (msg is not null)
{
http.Response.StatusCode = 200;
await http.Response.WriteAsJsonAsync(new { Message = msg });
}
else
{
http.Response.StatusCode = 404;
await http.Response.WriteAsync("Something went wrong...");
}
});
If I use for example Postman and post a request with the following random JSON body:
{
"qwe":"poasd",
"wer":"alkøsjda",
"hwata":"lkjasdfl",
"lkjdfgkljhdf":"5678"
}
the API returns a HTTP 200 with:
"message":
{
"receiver": "",
"content": ""
}
Why doesn't it throw an exception?

c# WebAPI API Issue - Swagger - Angular Axios

I have an issue and I got no idea what to do, the issue is that on my C# WebApi app with swagger enabled.
i have a few apis but here is an example 1 of them.
[HttpPost]
[Route("/api/user/register")]
public UserSession Register(string email, string password, string confirm_password)
{
if (password != confirm_password)
{
return new UserSession()
{
Success = false,
Message = "Error Passwords don't match",
SessionKey = "",
};
}
// success code here
}
here is the angular API.
import axios from 'axios';
export class API {
private static base_api:string = "http://localhost:51019/";
static register(email:string, password:any, confirm_password:any) {
let url = this.base_api + "api/user/register";
let data = {
email: email,
password: password,
confirm_password: confirm_password,
};
const headers = {
'Content-Type': 'application/json'
};
let result = axios.post(url, data, {headers}).then(x=>{return x}).catch(x=>{return false;});
console.log(result);
}
}
even when I provide an email and/or a password, it's like the API isn't receiving the data?
to fix the cor issue i had i added this to the api controller
[HttpOptions]
[Route("/api/user/register")]
[Route("/api/user/login")]
[Route("/api/user/logout")]
public HttpResponseMessage Options()
{
var response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.OK;
return response;
}
if i access the api via the swagger ui via (http://localhost:51019/swagger/index.html)
then when i perform the api via the UI it works correctly.
TIA
Typically you'd create a model and set it as the body:
public sealed class RegisterModel
{
[JsonPropertyName("email")]
public string Email { get; set; }
[JsonPropertyName("password")]
public string Password { get; set; }
[JsonPropertyName("confirm_password")]
public string ConfirmPassword { get; set; }
}
[HttpPost, Route("/api/user/register")]
public UserSession Register([FromBody] RegisterModel register)
{
// ...
}
That will get it to work in all scenarios

Get Data From Post Request Angular/.NET Core

I'm having trouble getting a JSON response from a POST Request from my .Net Core server. In essence I would be using this POST request like a GET request from the server. I believe I'm passing in the correct headers, however, in my console error I'm getting
ERROR TypeError: Cannot read property 'sessionId' of undefined
I suspect it's something that has to do with the type and/or model. Or possibly how I'm calling it in the service. If I need to add anything for clarification, lmk.
.NET CORE Server Code
Action.Dto
{
public class ActionDto
{
public string SessionId { get; set; }
public Tag ActionTag { get; set; }
public ActionParams Args { get; set; }
}
}
ActionService.cs
{
ActionResponse LaunchAction(string sessionId, Tag actionTag, ActionParams args, UserState userState);
}
Action Controller
public IActionResult LaunchAction([FromBody]ActionDto launchActionParameters)
{
var sessionId = launchActionParameters.SessionId;
var actionTag = launchActionParameters.ActionTag;
var args = launchActionParameters.Args;
UserState userState = null;
RunAction runAction = null;
Angular Client Code
Action Component
export interface ActionView {
actionName: string;
actionType: string;
primaryTable: string;
specialUse: string;
folder: string;
actionDescription: string;
actionTag: number;
chartType: string;
priority: number;
}
const ACTION_DATA: ActionView[] = [];
#Component({
templateUrl: 'home.component.html'
})
export class HomeComponent implements OnInit, OnDestroy {
// User Fields
currentUser: User;
users: User[] = [];
currentUserSubscription: Subscription;
// Action Fields
currentAction: Action;
actions: Action[] = [];
displayedColumns: string[] =
['actionName', 'actionType', 'primaryTable', 'specialUse',
'folder', 'actionDescription', 'actionTag', 'chartType',
'priority'];
dataSource: any = new MatTableDataSource(ACTION_DATA);
constructor(
private authenticationService: AuthenticationService,
private iconRegistry: MatIconRegistry,
private sanitizer: DomSanitizer,
private httpClient: HttpClient,
private actionService: ActionService
) {
this.currentUserSubscription = this.authenticationService.currentUser.subscribe(user => {
this.currentUser = user;
});
this.iconRegistry.addSvgIcon(
'thumbs-up',
this.sanitizer.bypassSecurityTrustResourceUrl('assets/img/examples/thumbup-icon.svg'));
}
#ViewChild(MatSort) sort: MatSort;
public getActions() {
console.log('test');
this.actionService.getActions(
this.currentAction).subscribe((data) => {
this.dataSource = data;
});
}
ngOnInit() {
this.dataSource.sort = this.sort;
this.getActions();
}
ngOnDestroy() {
// unsubscribe to ensure no memory leaks
this.currentUserSubscription.unsubscribe();
}
}
Action Service
#Injectable({ providedIn: 'root' })
export class ActionService {
public apiURL = 'http://localhost:15217/api';
public currentUser: Observable<User>;
public currentAction: Observable<Action>;
constructor(private http: HttpClient) { }
// Http Options
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
getActions(action: Action): Observable<Action[]> {
return this.http.post<Action[]>(this.apiURL + '/actions/launchactions',
{
sessionId: action.sessionId,
tag: action.actionTag,
actionParams: action.actionParams
})
.pipe(
retry(1),
catchError(this.handleError)
);
}
// Error handling
handleError(error: any) {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// Get client-side error
errorMessage = error.error.message;
} else {
// Get server-side error
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
window.alert(errorMessage);
return throwError(errorMessage);
}
add [FromBody] to controller side service, before the parameter. Post method pass parameters in body.
like
ActionResponse LaunchAction([FromBody]string sessionId, [FromBody]Tag actionTag, [FromBody]ActionParams args, [FromBody]UserState userState);
I don't know why microsoft did not decide to do this default.
You should remove {} from data: {}
By doing this you are assigning an empty object to that
What if you remove the "params" word from your angular http post call ?
So the http post call is this instead
return this.http.post<Action[]>(this.apiURL + '/actions/launchactions',
{
sessionId: action.sessionId,
tag: action.actionTag,
actionParams: action.actionParams
})
.pipe(
retry(1),
catchError(this.handleError)
);

Send raw JSON and file in the same web api 2 endpoint

I was wondering whether it is possible to send a file (which I only want ".pdf", ".jpg" or ".png") along with raw JSON.
All my endpoints so far send raw JSON (which I'm testing via Postman as the frontend does not exist yet), the intention is that the sending of form data will be sent using Angular Js. I don't know Angular Js yet, so I can't imagine how this will work.
The signature of the endpoint in question looks like this:
[Route("Post")]
[CustomAuthorize(Roles = "User, Admin")]
[ValidateJWT]
public async Task<IHttpActionResult> Post(HttpRequestMessage request, SalesOrderViewModel orderData)
The view model is just a c# class with loads of string properties that the model binder converts from JSON.
I would like to know whether sending raw JSON and a file that the user will select is possible in the same endpoint with Web API 2.
Is it?
Thanks in advance.
You can't direct with a post as aplication/json, still you can do it with multiple form fields (as form data), file + data, where the value of data can be a JSON.
I'm not recommending this method but does the trick:
public async Task<IHttpActionResult> Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
//load in a memory stream or in azure blob storage
var uploadFolder = "~/App_Data/FileUploads"; // to demonstrate the upload so please don't comment about where I'm saving the file, don't recommend this under no circumstance
var root = HttpContext.Current.Server.MapPath(uploadFolder);
Directory.CreateDirectory(root);
var provider = new MultipartFormDataStreamProvider(root);
var result = await Request.Content.ReadAsMultipartAsync(provider);
if (result.FileData.FirstOrDefault() == null)
{
return BadRequest("No import file was attached");
}
var uploadedFileInfo = new FileInfo(result.FileData.First().LocalFileName);
var model = result.FormData["model"];
if (model == null)
{
return BadRequest("Model is missing");
}
var parameters = JsonConvert.DeserializeObject<Coords>(model);
var byteArray = File.ReadAllBytes(uploadedFileInfo.FullName);
//..process the bytes
//..process json passed in headers
}
And the model:
public class Coords
{
public Cord[] cords { get; set; }
}
public class Cord
{
public int x { get; set; }
public object y { get; set; }
}
Postman call:
Edit: Just checked you don't have to use IEnumerable<byte>. Using byte[] works fine.
WebApi supports deserializing JSON array to IEnumerable<T>, so that you can receive bytes by declaring with IEnumerable<byte>.
The following example will show how to upload image:
public class ImageModel
{
public string Name { get; set; }
public IEnumerable<byte> Bytes { get; set; }
}
In your controller. Writing image to disk:
private string WriteImage(byte[] arr)
{
var filename = $#"images\{DateTime.Now.Ticks}.";
using (var im = Image.FromStream(new MemoryStream(arr)))
{
ImageFormat frmt;
if (ImageFormat.Png.Equals(im.RawFormat))
{
filename += "png";
frmt = ImageFormat.Png;
}
else
{
filename += "jpg";
frmt = ImageFormat.Jpeg;
}
string path = HttpContext.Current.Server.MapPath("~/") + filename;
im.Save(path, frmt);
}
return $#"http:\\{Request.RequestUri.Host}\{filename}";
}
HttpContext.Current.Server.MapPath("~/") will give the internal path of server running. Request.RequestUri.Host returns the hostname.
public IHttpActionResult UploadImage(ImageModel model)
{
var imgUrl = WriteImage(model.Bytes.ToArray());
// Some code
}
In HTML:
<input type="file" id="imageFile"/>
Upload method for AngularJS:
$scope.upload = function () {
var file = document.getElementById("imageFile").files[0];
var r = new FileReader();
r.onloadend = function (e) {
var arr = Array.from(new Uint8Array(e.target.result));
var uploadData = {
Name: "Name of Image",
Bytes: arr
}
console.log(uploadData);
$http.post('api/Uploader/UploadImage', uploadData)
.then(
function (response) {
console.log(response);
},
function (reason) {
console.log(reason);
})
}
r.readAsArrayBuffer(file);
}

How to write a post method which accepts JSON in Nancy and how to call it form a C# client?

I wrote following module in Nancy
public class CategoryModule : NancyModule
{
public CategoryModule()
{
//At this moment just Show Hello world
Get["/"] = _ => { return "Nancy says hello!"; };
//Get["/"] = parameters => "Hello World!";
GetCategories();
SetCategory();
}
void GetCategories()
{
Get["/Catergories"] = _ =>
{
var catergoryRepository = new CategoryRepository();
var categorycollection = catergoryRepository.GetCategoryInfo();
return Negotiate.WithStatusCode(HttpStatusCode.OK).WithModel(categorycollection.ToArray());
};
}
void SetCategory()
{
Post["/Catergories/{categryName:string}"] = _ =>
{
var catergoryModel = this.Bind<Category>();
catergoryModel.PK_CategoryId = Guid.NewGuid();
catergoryModel.CategoryName = _;
return HttpStatusCode.OK;
};
}
}
I am using chrome POSTMAN to test the Module.I can get the break point in "GetCategories()" if i call http://192.168.1.4:8888/Categories in POSTMAN.
But i couldn't get break point in SetCategory() if i call http://192.168.1.4:8888/Catergories/categryName=test. I am new to Nancy and not sure that my post method is correct.
could any one provide
An example to a Post method which accept Jason as parameter
An example to call it form client side
I couldn't find a simple example for above in their documentation.
Note
I am using Self hosting environment where i am hosting nancy with following code
var server = new Nancy.Hosting.Self.NancyHost(new Uri("http://192.168.1.4:8888"));
And following is the category model
public class Category
{
public Guid PK_CategoryId { get; set; }
public string CategoryName { get; set; }
}
If you want to post JSON with POSTMAN you should add headers with JSON content type, as docs say (third paragraph). Set up POSTMAN:
Set your host to http://192.168.1.4:8888/Categories and select POST.
Add headers with Content-Type and application/json as header and value
respectively.
Set raw and set JSON as type.
Put this { "CategoryName": "something" } as is in a text field below.
Hit "Send".

Categories

Resources