Windows Phone 72011. 11. 11. 15:27

아… 대단하다 이것 때문에 하루종일 샤워 할때도.. 꿈꿀때도 아침에 일어날때도 계속 이것을 어떻게 해결해야하나 싶었다.

현재 윈폰용 코레일 앱을 만드는데 EUC-KR에 이어 난항을 겪었다.

사건의 발단은 이렇다.

코레일에서 Rest Sevice를 이용해서 XML등을 읽어와서 파싱하는데

예약 현황 같은것은 로그인을 해야만 가능한것이였다.

웹에서를 예로 들면

로그인 인증 페이지 URL을 접속 해야만 예약 현황을 볼수 있는 페이지를 볼수 있었다.

만약 로그인 인증 페이지 URL을 먼저 접속하지 않으면 예약 현황 페이지에서는 로그인을 해야만 볼수 있다라고 뜬다.

그래서 내가 한방법은 Webclient를 이용해서 로그인 인증 페이지를 먼저 접속 후 예약 현황 페이지를 다시 호출한것이였다.

하지만 그 방법 역시 작동을 안했다. 구글링을 해서 좀더 로우레벨로 해보기로 하여 HttpWebRequest로도 해보고 아이폰쪽 소스도 보고 별짓을 다 했다.

결국 해결한것은 바로 쿠키였다 철도청 관계자는 서버 세션 유지라고 해서 난 쿠키랑 아무런 관계가 없을줄 알았다. 여기서 시간을 엄청 소비했다.

태규형님에게 도움을 요청했고 비슷한 사례를 하나 찾아주셧다.

http://bytes.com/topic/net/answers/426631-web-service-session-winform-client

결국난 CookieContainer 를 설정해야 한다는것을 깨닫고

Webclient를 override해서 가능하다는것을 깨달았다.

http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/149d545f-b605-4fce-b29a-2bfb1c51fbbe

하지만 그것은 WinForm에서는 작동했지만 윈폰에서는 약간의 보안문제로 다시 수정,보완을 하였다.

결국 성공.. 이틀동안의 삽질에 대한 눈물이 났다. ㅠㅠ

다음은 이틀 동안 고생해서 내가 만든 윈폰7용 소스이다. 이건 진짜 인터넷에 하나도 없다 ㅠㅠ

public class CookieAwareWebClient : WebClient
    {
        private CookieContainer m_container = new CookieContainer();

        [SecuritySafeCritical]
        public CookieAwareWebClient()
        {
        }

        protected override WebRequest GetWebRequest(Uri address)
        {
            WebRequest request = base.GetWebRequest(address);
            if (request is HttpWebRequest)
            {
                (request as HttpWebRequest).CookieContainer = m_container;
            }
            return request;
        }
    }

사용은 그냥

 _webClient.OpenReadAsync(new Uri(url));  _webClient.OpenReadCompleted 
 
를 사용하였다.             
Posted by 동동(이재동)
iPhone App2011. 9. 19. 11:01

보안이 필요한 데이터를 받고 줘야 할때 (로그인 인증같은거)

GET으로 하게 되면 보안이 치명적일수가 있다.

그래서 Post로 데이터를 받아서 XML을 파싱할려고 했지만

기본 NSXMLParser에는 Get방식으로는 받아서 바로 파싱할수 있으나 Post는 없었다.

그래서 할수 없이 NSURLConnectino을 이용하여 일단 url로부터 Data를 받은후 그 데이터를 NSXMLParser에 넣는 방식으로 처리해야 한다.


//url encoding

NSString *urlString =[NSString stringWithFormat:@"http://주소/MemberSelect?id=%@&pw=%@",_userID.text,_userPW.text];

NSString *escapedUrl = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

NSURL *xmlUrl =[NSURL URLWithString:escapedUrl];


이렇게 url을 세팅하고


  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:xmlUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];

[request setHTTPMethod:@"POST"];

[NSURLConnection connectionWithRequest:request delegate:self];

post로 보내면


- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

{

NSString *returnString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

  

NSLog(returnString);


XmlManager* xmlManager= [[XmlManager alloc] init];

NSMutableDictionary *loginInfoData= [xmlManager GetXmlDictDataByData:data elementFile:@"LogInElements.plist"];

}


이 delegate를 통해서 post로 보낸 데이터의 return값을 받을수 있다.


그래서 로그인이 성공하면 성공이라는 메세지를 xml로 보내주게 되고 그밑에 xml을 파싱했다.




Posted by 동동(이재동)
wcf2011. 8. 12. 14:04

일단 프로그램 추가 삭제에서 필요한거 asp.net wcf와 관련된것들을 다  체크 하고 설치를 하고


iis에서도 applciation pool 2.0으로 되어 있는것을 4.0으로 바꾸었으나


자꾸

HTTP Error 404.0 - Not Found

The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

이런 에러가 났었다. ㅡ.ㅡ;;


검색해보니

If ASP.NET v2.0.xxx does not appear in Web Services do:

start/Run/cmd:

cd C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727

(where Windows is your windows directory and .50727 is your .NET version)

aspnet_regiis –ir


이렇게 하란다고 해서 했더니 바로 됨 ㅡ.ㅡ;; 예전에도 이렇게 했었는데 까먹었음 

난 4.0이니 저위에 폴더를 4.0으로 가야 한다.

Posted by 동동(이재동)
iPhone App2011. 7. 22. 18:51

일단 내가 하고 싶었던것은 GET방식의 URL을 HttpRequest 를 해서 WCF에 전송하게 한뒤 글을 쓰게 만드는것이 목표였다.

일단 헤더에 NSMutableData를 만들고

-(void) save{

responseData = [NSMutableData new];

NSString *serviceURL = [NSString stringWithFormat:@"http://192.168.10.3:9090/hugeboardservice/write?name=%@&title=%@&description=%@",

[self encodeString:nameTextField.text],

[self encodeString:titleTextField.text],

[self encodeString:descriptionTextView.text]];


NSURL *url = [NSURL URLWithString:serviceURL];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];

  

[request setHTTPMethod:@"GET"];

  

[[NSURLConnection alloc] initWithRequest:request delegate:self];

[self.navigationController popViewControllerAnimated:YES];

}


이렇게 했다 저기 위에 보면 NSURLConnectino을 delegate 받았다.


그래서 추가적으로 4개의 델리게이트 메소드를 만들었다.


- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {

[responseData setLength:0];

}


- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

[responseData appendData:data];

}


- (void) connectionDidFinishLoading:(NSURLConnection *)connection {

[connection release];

  

NSString* responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];

NSLog(@"the html from google was %@", responseString);

  

[responseString release];

}


-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {

NSLog(@"something very bad happened here");

}


참고한곳은 http://toranbillups.com/blog/archive/2011/04/10/Writing-your-first-http-request-in-objective-c


여기이다.

하지만 이렇게 하다보니 문제가 생겼다 한글을 파라미터로 하면 오류가 났던것이다. 아마 인코딩을 안해서 그렇다고 생각해서


//한글떄문에 엔코딩하기 위해서 만든 메소드

-(NSString *)encodeString: (NSString*) unencodedString{

NSString *temp = (NSString *)CFURLCreateStringByAddingPercentEscapes(

NULL,

(CFStringRef)unencodedString,

NULL,

(CFStringRef)@"!*'();:@&=+$,/?%#[]",

kCFStringEncodingUTF8 );

  

return temp;

}


이런 메소드를 추가로 만들었다
만드는데 참고한 사이트는
http://simonwoodside.com/weblog/2009/4/22/how_to_really_url_encode/


***추가***

save를 하고 뒤로 가기 위해서 save메소드에 

[self.navigationController popViewControllerAnimated:YES];

이걸 넣었는데

여기에 넣는게 아니라 

- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { [responseData setLength:0]; [self.navigationController popViewControllerAnimated:YES]; }

이쪽에 넣는것이 맞다. 한마디로 작업이 완료가 되면 뒤로 넘어가야지 바로 넘어가면 안되는것이다.

참고 : http://www.pcraft.kr/101


Posted by 동동(이재동)
wcf2011. 6. 16. 15:57

내가 처한 사항은 이랬다.

 

모든것을 WCF Rest서비스로 구축하고 마지막으로 파일 업로드 를 구현하여 로컬에서 서비스를 돌려서

 

했더니 파일 업로드가 잘 작동 되었다.

 

근데 이게 왜 안되는지 IIS에만 서비스를 올리면 WebRequestError 400 Request 에러가 나는것이였다.

 

구글링을 했지만 너무 많은 경우에 수라 포기… 그렇다면 디버깅은 혹시 될까?

 

예전에 iis process(w3wp)를 잡아서 디버깅을 해본적이 있던터라 그걸로 해볼려고 했지만 까먹어서 실패 ㅋㅋ

 

그러다가 우연히 Microsoft Service Trace Viewer를 보게 되었다.

 

참고 :http://blogs.msdn.com/b/aszego/archive/2010/02/01/tool-of-the-month-servicemodeltraceviewer.aspx

 

이걸 사용하면 서비스에서 어떤일이 일어나는지 자세하게 알수 있겠구나 해서 실행을 해보았다.

 

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\SvcTraceViewer.exe에서 실행하면 된다(윈7)

 

자 실행을 하고 open을 할려니 svclog 확장자를 가진 파일이 필요하다.. 아이게 로그 파일이구나라고 순식간에 직감했다.

 

일단 web.config 에 이 부분을 추가한다.

<system.diagnostics>
    <sources>
      <source name="System.ServiceModel" switchValue="Information,ActivityTracing"
          propagateActivity="true">
        <listeners>
          <add name="ServiceModelTraceListener"/>
        </listeners>
      </source>
      <source name="System.Net.Sockets" switchValue="Information">
        <listeners>
          <add name="ServiceModelTraceListener"/>
        </listeners>
      </source>
      <source name="System.Net" switchValue="Information">
        <listeners>
          <add name="ServiceModelTraceListener"/>
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add initializeData="d:\client_tracelog.svclog"
          type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
          name="ServiceModelTraceListener" traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, ProcessId, ThreadId, Callstack"/>
    </sharedListeners>
    <trace autoflush="true" />
  </system.diagnostics>

 

추가 가 끝나면 저위에 경로 d:\client_tracelog.svclog에 기록될것이다.

 

이것을 open 하면 된다. 그럼 이렇게 에러를 잡을수 있다.

 

 

image

 

내가 왜 파일 업로드가 안되었는지 IIS에서만!!! 확인해보니 폴더 권한이 없었던 것이다!!

 

그래서 폴더 권한을 준후 다시 실행하니 잘되었다.

Posted by 동동(이재동)
wcf2011. 6. 16. 11:55

일단 WCF에서 보자

[WebInvoke(UriTemplate = "FileUpload?FileName={fileName}", Method = "PUT")]
       public void FileUpLoad(string fileName, Stream fileStream)
       {
           FileStream fileToupload = new FileStream("d:\\" + fileName, FileMode.Create);
 
           byte[] byteArray = new byte[10000];
           int byteRead, totalByteRead = 0;
           do
           {
               byteRead = fileStream.Read(byteArray, 0, byteArray.Length);
               totalByteRead += byteRead;
           } while (byteRead > 0);
 
           fileToupload.Write(byteArray, 0, byteArray.Length);
           fileToupload.Close();
           fileToupload.Dispose();
       }

 

일단 이렇게 메소드를 작성해서 file 이름과 stream을 받아서 파일을 경로에 쓴다.

 

method는 put으로 한다.

 

자 이제 끝이다. 이제 WPF에서 호출해보자.

 

 

 

public void ImageUpload(string fileName, string filePath)
        {
            using (WebClient webclient = new WebClient())
            {
                webclient.UploadData(new Uri(string.Format(serverUri + "/FileUpload?FileName={0}",fileName)), "put", GetData(filePath));
            }
        }
 
        private byte[] GetData(string filePath)
        {
            FileStream stream = File.OpenRead(filePath);
 
            byte[] data = new byte[stream.Length];
 
            stream.Read(data, 0, data.Length);
 
            stream.Close();
 
            return data;
        }

 

이렇게 filename과 byte로 변환된 스트림을 WCF로 날려주기만 하면 된다.

Posted by 동동(이재동)
wcf2011. 5. 31. 15:45

프로시저를 만들고 이제 WCF 서비스에서 프로시저를 실행을 하기 위해서 DB 연동을 해야 한다.

 

그냥 프로젝트에서 new item 후 Data 에 linq to sql classes를 선택 한다.

image

 

 

그뒤에 생성된 dbml에 Table과 프로시저를 드래그앤 드랍하면 끝~

image

 

호출은 이렇게 하면 된다.

 

public void InsertSubScriber()
        {
            using (DataClassesDataContext context = new DataClassesDataContext())
            {                
                context.sp_WP7Subscribers_Insert(1,"himan",DateTime.Now);
            }
        }

 

쉽다

Posted by 동동(이재동)
wcf2011. 5. 17. 15:57

자 Rest Service를 만들어 보자

 

RestService의 최대강점은  멀티 플랫폼할때 좋다는거다..

 

인자도 그냥 URL형식으로 Get,Post 방법으로 보내면 되고 그렇게 되면 언어나 플랫폼에 상관없이

 

쓸수 있는 아주 편한 진정한 RIA 서비스가 된다.

 

첫번째

 

new-project 해서 새로운 프로젝트를 만들고

 

Online Templates로 간다.. 아마 비쥬얼 스튜디오 2010을 깔았다면 그냥 wcf service가 있을것이다.

 

하지만 우리는 WCF REST Service를 만들것이기때문에 online Template으로 가서 프로젝트를 만든다.

 

image

 

만든뒤에

 

Service1.cs가 보일것이다.

 

물론  이걸써도 돼지만 새롭게 만드는것도 나쁘지 않다.

 

나는 윈폰에서 쓸 Notification Server를 만들꺼기때문에 Notifications.cs란 class를 새로 만들었다.

 

[ServiceContract]
   [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
   [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
   public class Notifications
   {
       private static List<Uri> subscribers = new List<Uri>();
       private static object obj = new object();
 
       [WebInvoke(UriTemplate = "register?uri={uri}", ResponseFormat = WebMessageFormat.Json, Method = "GET")]
       public void Register(string uri)
       {
           // TODO: Return the instance of SampleItem with the given id
           Uri channelUri = new Uri(uri, UriKind.Absolute);
           Subscribe(channelUri);
       }

 

그리고 클래스위에 ServiceContract를 정의 하였으며

 

저렇게 메소드를 만들어서 인자를 받게 만들었다..

 

그리고 중요한 Global.aspx.cs에서

 

 

private void RegisterRoutes()
{
    // Edit the base address of Service1 by replacing the "Service1" string below
    RouteTable.Routes.Add(new ServiceRoute("Service1", new WebServiceHostFactory(), typeof(Service1)));
    RouteTable.Routes.Add(new ServiceRoute("Notifications", new WebServiceHostFactory(), typeof(Notifications)));
}

 

 

이렇게 추가하고 컴파일한후

 

http://localhost:19976/notifications

 

컴파일 뒤에 notifications를 붙여서 접속하면 접속이 잘된다.

 

이제 머 메소드를 붙여 넣든 마음대로 요리하면 된다.

 

아참 이 Registor 메소드를 부르는쪽에서는 이렇게 호출한다.

 

/// <summary>
/// MS에서 받아온 url을 서버(WCF Service)에 보낸다.
/// </summary>
private void SubscribeToService()
{
    string baseUri = "http://localhost:19976/Notifications/Register?uri={0}";        
 
    string theUri = String.Format(baseUri, httpChannel.ChannelUri.ToString());
 
    WebClient client = new WebClient();
    client.DownloadStringCompleted += (s, e) =>
        {
             if (e.Error == null)
             {
                 Dispatcher.BeginInvoke(() => UpdateStatus("Registration Success"));
             }
             else 
             {
                 Dispatcher.BeginInvoke(() => UpdateStatus(e.Error.Message));
             }
 
        };
    client.DownloadStringAsync(new Uri(theUri));
}

 

 

쉽다.

Posted by 동동(이재동)