اشتراکها
اشتراکها
استخراج اطلاعات از Microsoft Band
- لطفا برای پرسش و پاسخ آن از این قسمت استفاده کنید.
- امکان تعریف سلول با فرمت Hyperlink وجود دارد. یک مثال. نکتهی مهم در نرمافزار Adobe، فرمت لینک به این فایل هست که حتما باید با پروتکل ///:file شروع شود تا قابل گشودن شود. برای این منظور مسیر محلی فایل باید از طریق new Uri(file).AbsoluteUri به مسیر پروتکل فایل تبدیل شود.
public async Task<IActionResult> FileUpload(IFormFile file) { if (file == null || file.Length == 0) { return BadRequest(); } using (var memoryStream = new MemoryStream()) { await file.CopyToAsync(memoryStream); var fileBytes = memoryStream.ToArray(); // ... save it } }
من در حین اجرا خط نهایی Action Result به خطای زیر برخورد میکنم
غیر از این فایل ها باید چیز دیگری هم بار اضافه کنم؟
Could not load file or assembly 'UsingBusinessObjects.dll' or one of its dependencies. The system cannot find the file specified.
غیر از این فایل ها باید چیز دیگری هم بار اضافه کنم؟
نظرات مطالب
اجرای کد از راه دور
سلام
به این روش RFI یا Remote File Inclusion گفته میشود.
مشکل از جایی ناشی میشود که برنامه نویس در کد خود include را به صورت پارامتری از ورودی قرار داده است. البته اگر Remote File Include در تنظیمات PHP غیر فعال باشد با استفاده از این روش هکر نمیتواند کاری انجام دهد(حتی با وجود باگ در کد)
برای بررسی phpinfo را چک کنید
به این روش RFI یا Remote File Inclusion گفته میشود.
مشکل از جایی ناشی میشود که برنامه نویس در کد خود include را به صورت پارامتری از ورودی قرار داده است. البته اگر Remote File Include در تنظیمات PHP غیر فعال باشد با استفاده از این روش هکر نمیتواند کاری انجام دهد(حتی با وجود باگ در کد)
برای بررسی phpinfo را چک کنید
پیشنیازها
«بررسی روش آپلود فایلها در ASP.NET Core»
«ارسال فایل و تصویر به همراه دادههای دیگر از طریق jQuery Ajax»
- در مطلب اول، روش دریافت فایلها از کلاینت، در سمت سرور و ذخیره سازی آنها در یک برنامهی ASP.NET Core بررسی شدهاست که کلیات آن در اینجا نیز صادق است.
- در مطلب دوم، روش کار با FormData استاندارد بررسی شدهاست. هرچند در مطلب جاری از jQuery استفاده نمیشود، اما نکات نحوهی کار با شیء FormData استاندارد، در اینجا نیز یکی است.
تدارک مقدمات مثال این قسمت
این مثال در ادامهی همین سری کار با فرمهای مبتنی بر قالبها است. به همین جهت ابتدا ماژول جدید UploadFile را به آن اضافه میکنیم:
همچنین به فایل app.module.ts مراجعه کرده و UploadFileModule را بجای UploadFileRoutingModule در قسمت imports معرفی میکنیم. سپس به این ماژول جدید، کامپوننت فرم ثبت یک درخواست پشتیبانی را اضافه خواهیم کرد:
که اینکار سبب به روز رسانی فایل upload-file.module.ts و افزوده شدن UploadFileSimpleComponent به قسمت declarations آن میشود.
در ادامه کلاس مدل معادل فرم ثبت نام یک درخواست پشتیبانی را تعریف میکنیم:
با این محتوا:
در اینجا Ticket تعریف شده دارای یک خاصیت توضیحات است و این فرم به همراه فیلد ارسال چندین فایل نیز میباشد که نیازی به درج آنها در کلاس فوق نیست:
ایجاد مقدمات کامپوننت UploadFileSimple و قالب آن
پس از ایجاد ساختار کلاس Ticket، یک وهله از آنرا به نام model ایجاد کرده و در اختیار قالب آن قرار میدهیم:
سپس قالب این کامپوننت و یا همان فایل upload-file-simple.component.html را به صورت ذیل تکمیل میکنیم:
در اینجا ابتدا فیلد توضیحات درخواست جدید، ارائه و به خاصیت model.description متصل شدهاست. همچنین این فیلد با ویژگی required مزین، و اجباری بودن آن بررسی گردیدهاست.
سپس در انتها، فیلد آپلود را مشاهده میکنید؛ با این ویژگیها:
الف) ngModel ایی به آن متصل نشدهاست؛ چون روش کار با آن متفاوت است.
ب) یک template reference variable به نام screenshotInput# در آن تعریف شدهاست. از این متغیر، در کامپوننت قالب استفاده خواهیم کرد.
ج) به رخداد change این کنترل، متد fileChange متصل شدهاست که رخداد جاری را نیز دریافت میکند.
د) ذکر ویژگی استاندارد multiple را نیز در اینجا مشاهده میکنید. وجود آن سبب خواهد شد تا کاربر بتواند چندین فایل را با هم انتخاب کند. اگر نیازی به ارسال چندین فایل نیست، این ویژگی را حذف کنید.
دسترسی به المان ارسال فایل در کامپوننت متناظر
تا اینجا یک المان ارسال فایل را به فرم، اضافه کردهایم. اما چگونه باید به فایلهای آن برای ارسال به سرور دسترسی پیدا کنیم؟
برای این منظور در ادامه دو روش را بررسی خواهیم کرد:
1) دسترسی به المان ارسال فایل از طریق رخداد change
در تعریف فیلد ارسال فایل، اتصال به رخداد change تعریف شدهاست:
معادل آن در سمت کامپوننت متناظر، به صورت ذیل است:
همانطور که مشاهده میکنید، event.target، امکان دسترسی مستقیم به المان متناظری را در قالب کامپوننت میسر میکند. سپس میتوان به خاصیت files آن دسترسی یافت.
در اینجا ساختار شیء استاندارد FileList و اجزای آنرا مشاهده میکنید. برای مثال چون دو فایل انتخاب شدهاست، این لیست به همراه یک خاصیت طول و دو شیء File است.
تعاریف این اشیاء استاندارد، در فایل ذیل قرار دارند و به همین جهت است که VSCode، بدون نیاز به تنظیمات دیگری، آنها را شناسایی و intellisense متناظری را مهیا میکند:
همچنین اگر به فایل tsconfig.json پروژه نیز مراجعه کنید، یک چنین تعاریفی در آن قرار دارند:
وجود و تعریف کتابخانهی dom است که سبب کامپایل شدن کدهای فوق، بدون بروز هیچگونه خطایی میشود.
2) دسترسی به المان آپلود فایل از طریق یک template reference variable
در حین تعریف المان فایل در فرم برنامه، متغیر screenshotInput# نیز ذکر شدهاست. میتوان به یک چنین متغیرهایی در کامپوننت متناظر به روش ذیل دسترسی یافت:
ابتدا یک خاصیت جدید را به نام screenshotInput از نوع ElementRef که در angular/core@ تعریف شدهاست، اضافه میکنیم. سپس برای اتصال آن به template reference variable ایی به نام screenshotInput، از ویژگی به نام ViewChild، با پارامتری مساوی نام همین متغیر، استفاده خواهیم کرد.
اکنون خاصیت screenshotInput کامپوننت، به متغیری به همین نام در قالب متناظر با آن متصل شدهاست. بنابراین با استفاده از خاصیت nativeElement آن همانند کدهایی که در متد submitForm فوق ملاحظه میکنید، میتوان به خاصیت files این کنترل ارسال فایلها دسترسی یافت.
نوع جدید و استاندارد HTMLInputElement نیز در فایل lib.dom.d.ts که پیشتر معرفی شد، ثبت شدهاست.
ارسال فرم درخواست پشتیبانی به سرور
تا اینجا فرمی را تشکیل داده و همچنین به فیلد file آن دسترسی پیدا کردیم. اکنون میخواهیم این اطلاعات را به سمت سرور ارسال کنیم. برای این منظور، سرویس جدیدی را ایجاد خواهیم کرد:
که سبب به روز رسانی خودکار قسمت providers فایل upload-file.module.ts نیز میشود.
در ادامه کدهای کامل این سرویس را مشاهده میکنید:
توضیحات تکمیلی:
روش کار با فرمهایی که فیلدهای ارسال فایل را به همراه دارند، متفاوت است با روش کار با فرمهای معمولی. در فرمهای معمولی، اصل شیء Ticket را به متد this.http.post واگذار میکنیم. مابقی آن خودکار است. در اینجا باید شیء استاندارد FormData را تشکیل داده و سپس اطلاعات را از طریق آن ارسال کنیم:
الف) افزودن مقادیر خواص شیء Ticket به FormData
با استفاده از حلقهی for میتوان بر روی خواص یک شیء جاوا اسکریپتی حرکت کرد. به این ترتیب میتوان نام و مقدار آنها را یافت و سپس به formData به صورت key/value افزود.
ب) افزودن فایلها به شیء FormData
پس از افزودن اطلاعات ticket به FormData، اکنون نوبت به افزودن فایلهای فرم است:
این مورد نیز به سادگی تشکیل یک حلقه، بر روی خاصیت files المان آپلود فایل است. به همین جهت بود که به دو روش سعی کردیم، به این خاصیت دسترسی پیدا کنیم.
یک نکته: چون در اینجا کلید اضافه شده، نام فایل است، دیگر نمیتوان در سمت سرور از روش model binding استفاده کرد. چون این نام دیگر ثابت نیست و هربار میتواند متغیر باشد (در حالت model binding دقیقا مشخص است که کلید مشخصی قرار است به سرور ارسال شود و بر همین اساس، نام خاصیت یا پارامتر سمت سرور تعیین میگردد). به همین جهت در سمت سرور برای دسترسی به این مجموعه، از روش Request.Form.Files استفاده میکنیم.
ج) ارسال اطلاعات نهایی به سرور
اکنون که formData را بر اساس اطلاعات اضافی ticket و فایلهای متصل به آن تشکیل دادیم، روش ارسال آن به سرور همانند قبل است:
یک نکته: در اینجا در روش استفاده از formData نباید Content-Type را به multipart/form-data تنظیم کرد. در غیراینصورت خطای Missing content-type boundary error را دریافت میکنید.
تکمیل کامپوننت ارسال درخواست پشتیبانی
پس از تکمیل سرویس ارسال اطلاعات به سمت سرور، اکنون نوبت به استفادهی از آن در کامپوننت ارسال فرم درخواست پشتیبانی است. بنابراین ابتدا این سرویس جدید را به سازندهی UploadFileSimpleComponent تزریق میکنیم:
و سپس متد submitForm چنین شکلی را پیدا میکند:
در اینجا this.model حاوی اطلاعات شیء ticket است (برای مثال اطلاعات توضیحات آن) و fileInput.files امکان دسترسی به اطلاعات فایلهای انتخابی توسط کاربر را میدهد. پس از آن فراخوانی متدهای this.uploadService.postTicket و subscribe، سبب ارسال این اطلاعات به سمت سرور میشوند.
دریافت فرم درخواست پشتیبانی در سمت سرور و ذخیرهی فایلهای آن
کدهای کامل SimpleUpload که در سرویس فوق مشخص شدهاست، به صورت ذیل هستند. ابتدا مدل Ticket مشخص شدهاست:
و سپس کنترلر ذخیره سازی اطلاعات Ticket را مشاهده میکنید:
توضیحات تکمیلی
- تزریق IHostingEnvironment در سازندهی کلاس کنترلر، سبب میشود تا از طریق خاصیت WebRootPath آن، به مسیر wwwroot سایت دسترسی پیدا کنیم و فایلهای نهایی را در آنجا ذخیره سازی کنیم.
- همانطور که ملاحظه میکنید، هنوز هم model binding کار کرده و میتوان شیء Ticket را به نحو متداولی دریافت کرد:
اما همانطور که عنوان شد، چون در حلقهی افزودن فایلها در سمت کلاینت، کلید نام این فایلها هربار متفاوت است:
مجبور هستیم در سمت سرور بر روی Request.Form.Files یک حلقه را تشکیل داده و تمام فایلهای رسیده را پردازش کنیم:
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
«بررسی روش آپلود فایلها در ASP.NET Core»
«ارسال فایل و تصویر به همراه دادههای دیگر از طریق jQuery Ajax»
- در مطلب اول، روش دریافت فایلها از کلاینت، در سمت سرور و ذخیره سازی آنها در یک برنامهی ASP.NET Core بررسی شدهاست که کلیات آن در اینجا نیز صادق است.
- در مطلب دوم، روش کار با FormData استاندارد بررسی شدهاست. هرچند در مطلب جاری از jQuery استفاده نمیشود، اما نکات نحوهی کار با شیء FormData استاندارد، در اینجا نیز یکی است.
تدارک مقدمات مثال این قسمت
این مثال در ادامهی همین سری کار با فرمهای مبتنی بر قالبها است. به همین جهت ابتدا ماژول جدید UploadFile را به آن اضافه میکنیم:
>ng g m UploadFile -m app.module --routing
>ng g c UploadFile/UploadFileSimple
در ادامه کلاس مدل معادل فرم ثبت نام یک درخواست پشتیبانی را تعریف میکنیم:
>ng g cl UploadFile/Ticket
export class Ticket { constructor(public description: string = "") {} }
ایجاد مقدمات کامپوننت UploadFileSimple و قالب آن
پس از ایجاد ساختار کلاس Ticket، یک وهله از آنرا به نام model ایجاد کرده و در اختیار قالب آن قرار میدهیم:
import { Ticket } from "./../ticket"; export class UploadFileSimpleComponent implements OnInit { model = new Ticket();
<div class="container"> <h3>Support Form</h3> <form #form="ngForm" (submit)="submitForm(form)" novalidate> <div class="form-group" [class.has-error]="description.invalid && description.touched"> <label class="control-label">Description</label> <input #description="ngModel" required type="text" class="form-control" name="description" [(ngModel)]="model.description"> <div *ngIf="description.invalid && description.touched"> <div class="alert alert-danger" *ngIf="description.errors.required"> description is required. </div> </div> </div> <div class="form-group"> <label class="control-label">Screenshot(s)</label> <input #screenshotInput required type="file" multiple (change)="fileChange($event)" class="form-control" name="screenshot"> </div> <button class="btn btn-primary" [disabled]="form.invalid" type="submit">Ok</button> </form> </div>
سپس در انتها، فیلد آپلود را مشاهده میکنید؛ با این ویژگیها:
الف) ngModel ایی به آن متصل نشدهاست؛ چون روش کار با آن متفاوت است.
ب) یک template reference variable به نام screenshotInput# در آن تعریف شدهاست. از این متغیر، در کامپوننت قالب استفاده خواهیم کرد.
ج) به رخداد change این کنترل، متد fileChange متصل شدهاست که رخداد جاری را نیز دریافت میکند.
د) ذکر ویژگی استاندارد multiple را نیز در اینجا مشاهده میکنید. وجود آن سبب خواهد شد تا کاربر بتواند چندین فایل را با هم انتخاب کند. اگر نیازی به ارسال چندین فایل نیست، این ویژگی را حذف کنید.
دسترسی به المان ارسال فایل در کامپوننت متناظر
تا اینجا یک المان ارسال فایل را به فرم، اضافه کردهایم. اما چگونه باید به فایلهای آن برای ارسال به سرور دسترسی پیدا کنیم؟
برای این منظور در ادامه دو روش را بررسی خواهیم کرد:
1) دسترسی به المان ارسال فایل از طریق رخداد change
در تعریف فیلد ارسال فایل، اتصال به رخداد change تعریف شدهاست:
(change)="fileChange($event)"
fileChange(event) { const filesList: FileList = event.target.files; console.log("fileChange() -> filesList", filesList); }
در اینجا ساختار شیء استاندارد FileList و اجزای آنرا مشاهده میکنید. برای مثال چون دو فایل انتخاب شدهاست، این لیست به همراه یک خاصیت طول و دو شیء File است.
تعاریف این اشیاء استاندارد، در فایل ذیل قرار دارند و به همین جهت است که VSCode، بدون نیاز به تنظیمات دیگری، آنها را شناسایی و intellisense متناظری را مهیا میکند:
C:\Program Files (x86)\Microsoft VS Code\resources\app\extensions\node_modules\typescript\lib\lib.dom.d.ts
{ "lib": [ "es2016", "dom" ] } }
2) دسترسی به المان آپلود فایل از طریق یک template reference variable
در حین تعریف المان فایل در فرم برنامه، متغیر screenshotInput# نیز ذکر شدهاست. میتوان به یک چنین متغیرهایی در کامپوننت متناظر به روش ذیل دسترسی یافت:
import { Component, OnInit, ViewChild, ElementRef } from "@angular/core"; export class UploadFileSimpleComponent implements OnInit { @ViewChild("screenshotInput") screenshotInput: ElementRef; submitForm(form: NgForm) { const fileInput: HTMLInputElement = this.screenshotInput.nativeElement; console.log("fileInput.files", fileInput.files); }
اکنون خاصیت screenshotInput کامپوننت، به متغیری به همین نام در قالب متناظر با آن متصل شدهاست. بنابراین با استفاده از خاصیت nativeElement آن همانند کدهایی که در متد submitForm فوق ملاحظه میکنید، میتوان به خاصیت files این کنترل ارسال فایلها دسترسی یافت.
نوع جدید و استاندارد HTMLInputElement نیز در فایل lib.dom.d.ts که پیشتر معرفی شد، ثبت شدهاست.
ارسال فرم درخواست پشتیبانی به سرور
تا اینجا فرمی را تشکیل داده و همچنین به فیلد file آن دسترسی پیدا کردیم. اکنون میخواهیم این اطلاعات را به سمت سرور ارسال کنیم. برای این منظور، سرویس جدیدی را ایجاد خواهیم کرد:
>ng g s UploadFile/UploadFileSimple -m upload-file.module
در ادامه کدهای کامل این سرویس را مشاهده میکنید:
import { Http, RequestOptions, Response, Headers } from "@angular/http"; import { Injectable } from "@angular/core"; import { Observable } from "rxjs/Observable"; import "rxjs/add/operator/do"; import "rxjs/add/operator/catch"; import "rxjs/add/observable/throw"; import "rxjs/add/operator/map"; import "rxjs/add/observable/of"; import { Ticket } from "./ticket"; @Injectable() export class UploadFileSimpleService { private baseUrl = "api/SimpleUpload"; constructor(private http: Http) {} private extractData(res: Response) { const body = res.json(); return body || {}; } private handleError(error: Response): Observable<any> { console.error("observable error: ", error); return Observable.throw(error.statusText); } postTicket(ticket: Ticket, filesList: FileList): Observable<any> { if (!filesList || filesList.length === 0) { return Observable.throw("Please select a file."); } const formData: FormData = new FormData(); for (const key in ticket) { if (ticket.hasOwnProperty(key)) { formData.append(key, ticket[key]); } } for (let i = 0; i < filesList.length; i++) { formData.append(filesList[i].name, filesList[i]); } const headers = new Headers(); headers.append("Accept", "application/json"); const options = new RequestOptions({ headers: headers }); return this.http .post(`${this.baseUrl}/SaveTicket`, formData, options) .map(this.extractData) .catch(this.handleError); } }
روش کار با فرمهایی که فیلدهای ارسال فایل را به همراه دارند، متفاوت است با روش کار با فرمهای معمولی. در فرمهای معمولی، اصل شیء Ticket را به متد this.http.post واگذار میکنیم. مابقی آن خودکار است. در اینجا باید شیء استاندارد FormData را تشکیل داده و سپس اطلاعات را از طریق آن ارسال کنیم:
الف) افزودن مقادیر خواص شیء Ticket به FormData
postTicket(ticket: Ticket, filesList: FileList): Observable<any> { const formData: FormData = new FormData(); for (const key in ticket) { if (ticket.hasOwnProperty(key)) { formData.append(key, ticket[key]); } }
ب) افزودن فایلها به شیء FormData
پس از افزودن اطلاعات ticket به FormData، اکنون نوبت به افزودن فایلهای فرم است:
for (let i = 0; i < filesList.length; i++) { formData.append(filesList[i].name, filesList[i]); }
یک نکته: چون در اینجا کلید اضافه شده، نام فایل است، دیگر نمیتوان در سمت سرور از روش model binding استفاده کرد. چون این نام دیگر ثابت نیست و هربار میتواند متغیر باشد (در حالت model binding دقیقا مشخص است که کلید مشخصی قرار است به سرور ارسال شود و بر همین اساس، نام خاصیت یا پارامتر سمت سرور تعیین میگردد). به همین جهت در سمت سرور برای دسترسی به این مجموعه، از روش Request.Form.Files استفاده میکنیم.
ج) ارسال اطلاعات نهایی به سرور
اکنون که formData را بر اساس اطلاعات اضافی ticket و فایلهای متصل به آن تشکیل دادیم، روش ارسال آن به سرور همانند قبل است:
const headers = new Headers(); headers.append("Accept", "application/json"); const options = new RequestOptions({ headers: headers }); return this.http .post(`${this.baseUrl}/SaveTicket`, formData, options) .map(this.extractData) .catch(this.handleError);
یک نکته: در اینجا در روش استفاده از formData نباید Content-Type را به multipart/form-data تنظیم کرد. در غیراینصورت خطای Missing content-type boundary error را دریافت میکنید.
تکمیل کامپوننت ارسال درخواست پشتیبانی
پس از تکمیل سرویس ارسال اطلاعات به سمت سرور، اکنون نوبت به استفادهی از آن در کامپوننت ارسال فرم درخواست پشتیبانی است. بنابراین ابتدا این سرویس جدید را به سازندهی UploadFileSimpleComponent تزریق میکنیم:
import { UploadFileSimpleService } from "./../upload-file-simple.service"; export class UploadFileSimpleComponent implements OnInit { constructor(private uploadService: UploadFileSimpleService ) {}
submitForm(form: NgForm) { const fileInput: HTMLInputElement = this.screenshotInput.nativeElement; console.log("fileInput.files", fileInput.files); this.uploadService .postTicket(this.model, fileInput.files) .subscribe(data => { console.log("success: ", data); }); }
دریافت فرم درخواست پشتیبانی در سمت سرور و ذخیرهی فایلهای آن
کدهای کامل SimpleUpload که در سرویس فوق مشخص شدهاست، به صورت ذیل هستند. ابتدا مدل Ticket مشخص شدهاست:
namespace AngularTemplateDrivenFormsLab.Models { public class Ticket { public int Id { set; get; } public string Description { set; get; } } }
using System.IO; using System.Threading.Tasks; using AngularTemplateDrivenFormsLab.Models; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; namespace AngularTemplateDrivenFormsLab.Controllers { [Route("api/[controller]")] public class SimpleUploadController : Controller { private readonly IHostingEnvironment _environment; public SimpleUploadController(IHostingEnvironment environment) { _environment = environment; } [HttpPost("[action]")] public async Task<IActionResult> SaveTicket(Ticket ticket) { //TODO: save the ticket ... get id ticket.Id = 1001; var uploadsRootFolder = Path.Combine(_environment.WebRootPath, "uploads"); if (!Directory.Exists(uploadsRootFolder)) { Directory.CreateDirectory(uploadsRootFolder); } var files = Request.Form.Files; foreach (var file in files) { //TODO: do security checks ...! if (file == null || file.Length == 0) { continue; } var filePath = Path.Combine(uploadsRootFolder, file.FileName); using (var fileStream = new FileStream(filePath, FileMode.Create)) { await file.CopyToAsync(fileStream).ConfigureAwait(false); } } return Created("", ticket); } } }
- تزریق IHostingEnvironment در سازندهی کلاس کنترلر، سبب میشود تا از طریق خاصیت WebRootPath آن، به مسیر wwwroot سایت دسترسی پیدا کنیم و فایلهای نهایی را در آنجا ذخیره سازی کنیم.
- همانطور که ملاحظه میکنید، هنوز هم model binding کار کرده و میتوان شیء Ticket را به نحو متداولی دریافت کرد:
SaveTicket(Ticket ticket)
formData.append(filesList[i].name, filesList[i]);
var files = Request.Form.Files; foreach (var file in files)
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
معرفی:
امروزه تست کردن کدها به دلیل وجود ابزارهای مختلف زیادی، کار آسانی شده است. اما بعضیها در web application ها، یکی از تستهایی را که خیلی هم مهم است را فراموش میکنند که آن هم تست UI است. شما را در این مقاله با یکی از روشهای خوب تست UI آشنا خواهم کرد. ابزارهای زیادی برای تست UI وجود دارد که کار کردن با آنها نه تنها زمان بر بلکه بسیار خسته کننده میباشند و به خاطر همین خیلیها از انجام تست UI صرف نظر میکنند.
WatIn چیست؟
WatIn مخفف Web Application Testing in .Net میباشد؛ که یک فریم ورک تست web applicationها است. WatIn این اجازه را به شما میدهد که با استفاده از IE ویا FireFox عناصر داخل صفحات را مقدار دهی کنید و یا حتی رویدادی را برای عناصر فراخوانی کنید.
شروع کار با WatIn:
در زیر یک نمونه از کار با WatIn را میتوانید مشاهده کنید:
[TestMethod] public void SearchForWatiNOnGoogle() { using (var browser = new IE("http://www.google.com")) { browser.TextField(Find.ByName("q")).TypeText("WatiN"); browser.Button(Find.ByName("btnG")).Click(); Assert.IsTrue(browser.ContainsText("WatiN")); } }
WatIn یک فریم ورک کاربر پسند است و در ادامه متوجه میشوید که استفاده از این فریم ورک چه مزایایی دارد. برای نصب، WatIn را میتوانید از اینجا دانلود کنید ویا اگر خواستید میتوانید با NuGet هم این فریم ورک را دانلود کرده و نصب نمایید. برای شروع کار با Watin باید reference هایی را به پروژه تان اضافه کنید که یکی از این referenceها WatiN.Core.dll میباشد و برای استفاده از IE ویا FireFox باید فضای نام Watin.Core را اضافه کنیم. Watin چند فضای نام دیگری را هم به همراه دارد که در زیر به توضیح مختصری از آنها میپردازیم:
1- Watin.Core.DialogHandlers: این فضای نام این امکان را به شما میدهد تا دیالوگ هایی را که مرورگر میتواند به کاربر نمایش دهد، مدیریت کنید. از handlerهای این فضای نام AlertDialogHandler، ConfirmDialogHandler، FileUploadDialogHandler، PrintDialogHandler و LoginDialogHandler میباشد.
2- Watin.Core.Exceptions: این فضای نام دارای یک سری exception میباشد و این امکان را به ما میدهد تا یک سری رفتارهای ناخواسته را کنترل کنیم. بعضی از این exceptionها ElementNotFoundException، IENotFoundException، TimeoutException و WatinException میباشد.
3- Watin.Core.Logging: این فضای نام کلاس هایی را در اختیار ما میگذارد تا بتوانیم عملیاتی را که در کدمان انجام میدهیم log کنیم.
مثالی از watin که در بالا نشان دادیم به این صورت عمل میکند که مرورگر IE را باز کرده و به سایت google خواهد رفت. در این صفحه جعبه متنی یا TextBox با نام "q" را پیدا کرده و عبارت "Watin" را در آن تایپ میکند و همچنین Buttonی با نام "btnG" پیدا کرده و آن را کلیک مینماید و در آخر بررسی میکند که در مرورگر متنی شامل WatIn وجود دارد یا خیر.
مشاهده کردید که به همین سادگی یک تست UI نوشتیم. به نظر شما جالب نبود؟ فرض کنید که اگر میخواستید با مثلا Microsoft Test Manager این کار را انجام دهید چه دردسرهایی را باید تحمل میکردید. حالا تست UI برای همه برنامه نویسها جذاب خواهد شد.
به جای مثال بالا میتوانیم به صورت زیر هم عمل کنیم:
[TestMethod] public void SearchForWatiNOnGoogle() { using (var browser = new IE("http://www.google.com")) { browser.TextField(Find.ByName("q")).Value="WatiN"; browser.Button(Find.ByName("btnG")).ClickNoWait(); Thread.Sleep(3000); Assert.IsTrue(browser.ContainsText("WatiN")); } }
تفاوت کد دوم با کد اول این است چون در کد اول از متد TypeText استفاده کردیم یک مقدار سرعت تست را پایین میآورد ولی اگر از Value ویا از SetAttribute استفاده کنیم دیگر عمل تایپ را انجام نداده و مقدار را مستقیما در مقدار TextField قرار میدهد. شاید بپرسید چرا بعد از متد ClickNoWait چند ثانیه صبر میکنم؟ چون صفحه برای اینکه بارگذاری شود و نتیجه جستجو را نشان دهد کمی طول کشیده و Assert.IsTrue شما Failed میشود. البته به جای Thread.Sleep میتوانیم از متدهای مربوط به Watin هم استفاده کنیم مانند WaitUntilComplete ویا از WaitUntilContainsText.
مطالب
معرفی افزونه CAT.NET
اخیرا مایکروسافت افزونه رایگانی را برای آنالیز امنیتی کدهای برنامههای نوشته شده با VS.Net ارائه داده است به نام CAT.Net.
دریافت افزونه 32 بیتی، 64 بیتی
این افزونه قابلیت بررسی کدهای شما را جهت یافتن خطرات جدی SQL Injection ، XSS و XPath Injection دارد.
نحوه استفاده:
VS.Net خود را ببندید. در ادامه، پس از نصب، به منوی Tools و گزینهی جدید CAT.NET Code Analysis مراجعه نمائید.
صفحهای ظاهر خواهد شد که پس از کلیک بر روی دکمه آغاز آنالیز آن، کار بررسی امنیتی پروژه را آغاز میکند (شکل زیر)
این افزونه همانند FxCop ، اسمبلی برنامه را آنالیز میکند. پس از پایان آنالیز، با کلیک بر روی هر سطری که گزارش داده، آن سطر را میتوان در پروژه یافت و تغییرات لازم را اعمال نمود.
همچنین پس از پایان کار بررسی، یک فایل xml هم در مسیر فایلهای پروژه ایجاد میکند که در آینده در صورت نیاز، توسط همین افزونه قابل گشودن است.
بعلاوه پس از نصب، یک فایل chm هم در دایرکتوری آن جهت آشنایی بیشتر با اجزای مختلف این افزونه قرار خواهد گرفت.
البته، برنامه هنوز در مراحل آزمایشی به سر میبرد. برای مثال پس از نصب در مسیر دیگری غیر از مسیر پیش فرض نصب، پیغام میداد که فایلها را نمیتواند پیدا کند. اگر این مورد برای شما هم رخ داد، مسیری را که گزارش میدهد به صورت دستی درست کنید و فایلهای کانفیگ آنرا از جایی که نصب کردهاید به آنجایی که برنامه به شما گزارش میدهد کپی کنید تا کار کند!
یا میتوان این افزونه را از طریق خط فرمان هم اجرا کرد (مسیر پروژه، اسمبلی آن و مسیر نصب cat.net را لازم دارد). به صورت زیر:
CATNETCmd /file:"I:\prog\bin\prog.dll" /search:"I:\prog" /report:"I:\prog\report.xsl" /rule:"J:\microsoft\cat.net\Rules"
اگر علاقمند به مطالعه تاریخچهی این برنامه هستید به وبلاگ زیر مراجعه نمائید:
مشاهده وبلاگ
اشتراکها
152 نکتهی Postgres SQL
152 psql Tips — This was originally 100 tips when we first included it, but this list of bite-sized tips for the tried and tested psql client continues to grow. If you have a psql session coming up any time soon, this is worth a browse. It’s also possible to load a single tip and hop through randomly if you prefer your tips snack-sized.