RPNetworks.Configuration - 01

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (12/4/2010 2:06:13 PM) Viewing : 1722

asp.net으로 웹사이트를 만들때나 windows 응용프로그램을 만들때 각각의 config 파일에 환경 변수 같은 값을 저장해 놓고 쓰곤 합니다.

일전에 아쉽지만 잠깐 다루어 본적이 있습니다.

먼저 http://www.bangsil.net/Tag/Configuration 을 참고 하심이 좋을 듯 합니다.

첨부된 템플릿 파일의 ConfigurationHelper 클래스의 주석 부분을 보면 간단한 사용법이 나와 있네요..그대로 한번 옮겨 보겠습니다.

1. 사용자 정의 환경 설정을 사용하기 위해서는 아래와 같이 configuration섹션 내부에 config 구성이 이루어져야 합니다.

<CONFIGSECTIONS>    
 <SECTIONGROUP name="rpnetworks" 
               type="RPNetworks.Configurations.AncestorSectionGroup, RPNetworks.Configurations">    
  <SECTION name="dbConnection" 
           type="RPNetworks.Configurations.DBConnectionSection, RPNetworks.Configurations" />    
  <SECTION name="monitoring" 
           type="RPNetworks.Monitoring.CustomMonitoringSection, RPNetworks.Monitoring" />    
  <SECTION name="projectInfo" 
           type="RPNetworks.Configurations.ProjectInfoSection, RPNetworks.Configurations" />    
  <SECTIONGROUP name="testProject" 
                type="RPNetworks.Configurations.CustomConfigurationSectionGroup
                       ,RPNetworks.Configurations">    
   <SECTION name="network" type="사용자정의네임스페이스.NetworkSection, 사용자정의네임스페이스" />    
  </SECTIONGROUP>    
 </SECTIONGROUP>    
</CONFIGSECTIONS>

2. 섹션 및 섹션그룹에 대한 설정이 마무리 되면 이제 아래와 같은 코드로 설정을 하여야 합니다.

 
  
  
  
  
  
  
  
  
   
    
   
   
  

3. 이제 설정된 사용자 정의 섹션 testProject에 대한 섹션클래스를 생성하여야 합니다.

public class NetworkSection : CustomConfigurationSection
{
    internal NetworkSection()
        : base()
    {
        this.SectionInformation.ForceSave = true;
    }
    [ConfigurationProperty("domain", IsRequired = true)]
    public string Domain
    {
        get
        {
            return (string)base["domain"];
        }
    }
    [ConfigurationProperty("smtp")]
    public CustomConfigurationElementCollection Smtp
    {
        get
        {
            return (CustomConfigurationElementCollection)base["smtp"];
        }
    }
    protected override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey)
    {
        if (writer == null)
        {
            return false;
        }
        writer.WriteAttributeString("domain", this.Domain.ToString());
        return true;
    }
    protected override bool SerializeToXmlElement(XmlWriter writer, string elementName)
    {
        bool ret = base.SerializeToXmlElement(writer, elementName);
        if (writer == null)
        {
            return false;
        }
        writer.WriteStartElement(elementName);
        this.SerializeElement(writer, false);
        this.Smtp.GetType().InvokeMember("SerializeToXmlElement", 
            BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, 
            null, this.Smtp, new object[2] { writer, "smtp" });
        writer.WriteEndElement();
        return true;
    }
}

4. 다음과 같이 환경설정 값들을 조회 할 수 있습니다.

string domain = ConfigurationHelper.Project.GetSection("network").GetParameter("domain");
string server = ConfigurationHelper.Project.GetSection("network")
                .GetElements("smtp").Parameters["server"];

이상입니다.

Configuration 파트를 읽어 보시고 이해가 되었다면 별로 어려운 코드는 아니라고 생각 됩니다.

기본은 이정도 이니 다음 글부터는 노드 아래에 있는 projectinfo 섹션 부터 하나씩 알아 보도록 하지요.


마지막 업데이트 : (1/5/2011 5:32:39 PM)




첨부 파일 보기 (1)
Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기

RPNetworks.Core - SqlDB 클래스

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (12/1/2010 8:23:33 PM) Viewing : 1824

첨부 파일은 템플릿으로 제공됩니다.

템플릿에 대한 설명은 예전 게시글 중 댓글을 참고 하시면 되겠습니다.

예전 버전에 비해 업데이트가 좀 있었습니다.

가장 큰 이슈는 연결 문자열의 암호화를 느슨한 연결로 변경한 부분이 되겠습니다.

그리고 주석도 좀 보강을 했지요.

먼저 생성자 부분을 보겠습니다.

/// 
///   클래스의 생성자입니다. 기본값은 암호화를 사용하지 않습니다.
/// 
/// Config파일에서 정의된 connectionString의 name 값입니다
/// config 파일에 다음과 같이 정의되어 있어야 합니다.
/// 
///   
///    
///     
///      
///      
///     
///    
///   
///  
/// 생성자는 다음과 같이 호출 됩니다.
/// 
/// SqlDB db = new SqlDB("con");
/// 
/// 
public SqlDB(string connectionString)
{
    this.connection = new SqlConnection(ConfigurationHelper.GetConnectionString(connectionString));
}

xml 주석으로 도움말 파일을 생성하게 되면 위와 같은 주석이 도움말에 포함이 되죠.. http://bangsil.net/Tag/주석 을 참고 하십시요.

cinfig 설정에 대한 부분은 추후 Cofiguration 파트에서 다시 설명하도록 하겠습니다.

위 코드는 위 예시에 나온 것 처럼 xml 파일에 정의된 암호화된 문자열을 가져와서 복호화 해서 연결문자열을 만든다는 정도로만 이해 하시면 될듯 합니다.

이제 생성자로 개체를 생성 했으니 어떻게 사용할 까요..

예제를 보시죠..

 SqlDB db = new SqlDB("con");
 bool hasError = false;
 SqlParameter[] param1 = {
             new SqlParameter("@RETURNVALUE", SqlDbType.Int),
             new SqlParameter("@DATA_INTERFACE_TYPE_CODE", SqlDbType.Char, 1)
         };
 param[0].Direction = ParameterDirection.ReturnValue;

 try
 {        
     db.BeginTransaction();
 
     db.ExecuteNonQuery(false, false, CommandType.StoredProcedure
                        , "[dbo].[SP_NOTI_ADD_MEETING]", param1, flag);
     int returnValue = db.GetReturnValue(param1[0]);
 
     if(returnValue == 0)
     {
         foreach (JoinedUserItem joinedUserItem in joinedUsers)
         {
             SqlParameter[] param = {
                new SqlParameter("@DATA_INTERFACE_TYPE_CODE", SqlDbType.Char, 1),
                new SqlParameter("@ITEM_ID", SqlDbType.NVarChar, 27)
             };
             db.ExecuteScalar(false, false, CommandType.StoredProcedure
                           , "[dbo].[SP_EP_INTERFACE_ADD_RESERVATION_INFO]"
                           , param, "I", "M" + item.MeetingSeq);
         }
     }
 }
 catch (Exception ex)
 {
     hasError = true;
     db.RollBackTransaction();
     throw ex;
 }
 finally
 {
     if (!hasError)
     {
         db.CommitTransaction();
     }
     db.Dispose();
 }

위 예제는 너무 직관적(?) 이라 따로 설명이 필요 없을 듯 합니다만..조금 설명을 덧 붙여 보겠습니다.

BeginTransaction() 메서드는 프로그래밍적으로 트랜잭션을 이용할 때 이용합니다.
프로그래밍적으로 트랜잭션을 사용하면 프로시저 단에서는 이를 고려하여야 겠죠?

위 예제 처럼 처음 연결 후 트랜잭션을 열고 여러번의 쿼리를 한 후에 롤백 혹은 커밋을 할 수 있습니다.

이때 주의 점은 ExecuteNonQuery 와 같은 쿼리 메서드의 파라메터 설정입니다.(Execute~  메서드는 모두 대동소이합니다.)

BeginTransaction() 메서드를 통해 트랜잭션을 사용하는 경우 Execute~ 메서드의 파라메터 중 isTransaction 값은 반드시 false 여야 합니다. 이 값이 true 일 때면 해당 쿼리에서만 트랜잭션을 사용해야 할 경우입니다.

또 주의 해야 할 파라메터는 mustCloseConnection 의 값 또한 위 예제 처럼 false 가 되어야 합니다.

이 값이 true 라는 것은 이 쿼리가 종료되는 연결을 종료 한다는 의미가 되겠습니다.

위 예제 같은 경우 이 값이 true가 되어 버리면 첫 쿼리 이후에 연결을 끊어 버려 논리적인 오류가 발생되겠지요..(런타임 오류가 아닙니다.) 그리고 트랜잭션 수만 증가가 되겠지요 커밋이나 롤백을 하지 않았으니까요.

14행 처럼 GetReturnValue() 제너릭 메서드를 통해 리턴값을 받아 올 수도 있습니다.

7행 처럼 파라메터의 Direction 을 설정하고 14행 처럼 받아 오면 됩니다.

물론 프로시저에서 RETURN 을 해줘야 겠지요.

Async 메서드와 DataSet의 Update메서드도 있긴 하지만 사용빈도가 현저히 낮기 때문에 자질구레한 설명은 생략 합니다.


마지막 업데이트 : (1/5/2011 5:19:31 PM)




첨부 파일 보기 (1)
Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기

jquery.MultiFile 버그 수정하기

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (12/1/2010 3:56:05 PM) Viewing : 3669

jquery 를 이용한 멀티파일 업로드 플러그인 중에 아마도 가장 많이 쓰는 플러그인이 제목과 같은 jquery.MultiFile  플러그인이 아닐까 싶습니다.

물론 이 사이트에서도 이 플러그인을 사용하고 있습니다.

여지껏 몰랐었는데..이 플러그인에게는 치명적인 단점이 있었네요..ㅡ.ㅡ;

문제점을 재현해 볼까요.

파일 첨부를 위해 찾아 보기 버튼을 클릭 하고 활성화 되는 다이얼로그 윈도우에서 파일을 선택합니다.

그러면 현재 상태는 file 컨트롤에 조금 전에 선택한 파일의 경로가 보이게 되죠..

이 상태에서 다시 한번 바로 찾아 보기 버튼을 클릭하여 파일을 선택합니다.

그러면 두번째 선택한 파일이 file  컨트롤에 보입니다.

이제 아무곳이나 클릭 합니다.

그렇게 되면 file 컨트롤이 2개가 보입니다..쿨럭....

이 현상은 jquery의 이벤트가 실행되는 시간에 관련된 문제라고 추측됩니다.

문제의 원인은 이 플러그인 코드를 조금만 분석해 보면 보입니다.

파일을 선택하게 되면 jquery의 change() 이벤트가 발생되어 이 이벤트에서 기존에 파일을 선택한 file 컨트롤을 top -3000px 로 위치 시키고 현재 위치에 새로운 file 컨트롤을 동적으로 생성합니다.

물론 사용자는 못 느끼죠.

그런데 여기서 문제가 발생합니다.

change()이벤트는 즉시 일어 나지 않습니다.

onChange 이벤트가 즉시 일어나는데 반해 change() 이벤트는 즉시 일어 나지 않기에 하나의 파일을 선택한 후에 바로 즉시 다시 찾아 보기 버튼을 클릭 하게 되면 최초에 생긴 file 컨트롤이 이벤트를 받게 되는 것입니다.(동적으로 새로 생긴 file 컨트롤이 이벤트를 받아야 하는데 말이죠..)

이런 현상때문에 아주 우스꽝 스러운 모습을 보이게 되고 실제로 서버에서도 Request.Files 속성을 통해서 보게 되면 파일이 누락되어 있음을 확인 할 수 있습니다.

이를 해결 하기 위해서는 원칙에 어긋나지만 (원칙은 html 태그에는 이벤트 바인딩을 안하는 것) file 컨트롤에 onChange() 이벤트를 등록하는 것입니다.

그리고 스크립트에서는 다음과 같이 처리하면 됩니다.

        function fileChange(ctl) {
            $(ctl).css('display', 'none');
        }

그러면 이제 찾아 보기 버튼을 클릭 한 후에 파일을 선택하게 되면 그 즉시 change() 이벤트가 발생하게 됩니다.


마지막 업데이트 : (2/10/2012 5:33:21 PM)




Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기

MVC 응용 프로그램 배포 하기(특히 호스팅 업체에..)

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (11/17/2010 3:28:25 PM) Viewing : 6157

벗의 부탁으로 제 소스를 조금 개조하여 호스팅 업체에 배포 할 기회가 생겼습니다.

물론 .NET Framework 3.5 가 설치된 업체를 선정 했습니다.

값도 싸고 해서 나누미넷 (http://www.nanuminet.com) 을 선정했습니다.

그런데 실행이 안되어서 애를 좀 먹었네요..ㅎㅎ

나누미넷은 싼건 좋은데 닷넷 전문가는 없더군요..ㅡ.ㅡ;;;; 여튼...

http://msdn.microsoft.com/ko-kr/library/dd410407.aspx

위 MSDN에 의하면 SP1이 설치가 되어 있지 않아도 3.5 만 설치되어 있으면 실행 가능 합니다.

자 실제로 위 가이드에 나온 것 처럼..해 보았습니다..

잘 되었을까요???

페이지 안뜨더군요..ㅋㅋ 이때 부터 개고생 시작입니다..ㅡ.ㅡ;

만약 404, 403, 디렉터리를 나열할 수 없습니다  라는 메시지 중에 하나가 보인다면...거의 근접한겁니다.

라우터가 안먹힌다는 얘기가 되겠습니다.

해결법은 간단합니다.

ISAPI 에 *를 등록하면 되겠습니다.

그렇게 되면 MVC 라우터가 작동을 시작합니다.

이제 페이지가 보이겠지요????

 

추가  ISAPI 등록 법

IIS 6 의 경우입니다.

홈디렉터리 -> 구성을 클릭 -> 매핑 탭에서 와일드카드 응용 프로그램 매핑 에 삽입 클릭

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll 선택 하고 파일이 있는지 확인에 언체크

이제..됩니다..^^


마지막 업데이트 : (3/8/2011 4:08:51 PM)

TAG : 배포 MVC 



Trackback 보기 (0)
댓글 보기 (9)
유진상님의 글 (4/4/2011 6:38:11 PM)
ISAPI에 확장명에 .* 또는 *를 넣게 되면 "잘못된 확장명 형식입니다" 라고 나타나는데

어떻게 해야하나요?

제발 제 메일로좀 알려주세요 ㅜㅜ

jinsang83@bit.co.kr



유진상님의 글 (4/4/2011 6:45:48 PM)
IIS 6.0 입니다.
.net framework 4.30319 이며

왜 저는 응용 프로그램 확장 매핑 추가.편집으로 * 안델까요 ㅡㅜ



방실이님의 글 (4/5/2011 9:15:51 AM)
위 글 내용 끝부분을 보시면...
홈디렉터리 -> 구성을 클릭 -> 매핑 탭에서 와일드카드 응용 프로그램 매핑 에 삽입 클릭

요렇게 나와 있네요..^^ 다시 따라 해보세요..

유진상씨는 안면이 있는거 같은데..^^;



유진상님의 글 (4/5/2011 10:59:46 AM)
우선 Notice에서 KIMES 2008년 유헬스케어를 통해 출품하셨다고 하셔서 경쟁사인가해서

보게 되었는데요 우연히도 비트 컴퓨터에 재직하셨더라구요.

현재 재직중인 분 몇분도 아시구요

제가 궁금해하는걸 해결하신 대다 비트라는 말에 정말 정말 반가웠습니다.

그럼 본론으로

"ISAPI 에 *를 등록하면 되겠습니다." 가 궁금해서요

이게

홈디렉터리 -> 구성을 클릭 -> 매핑 탭에서 "추가" 버튼을 눌러서

실행 파일에는 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll

동사는 모든 동사, 파일이 있는지 확인은 언체크

그뒤!! 확장명 칸에는 *를 입력하는게 맞는지요?

하단에 있는 도움말 버튼을 눌러 보면

"확장명은 응용 프로그램에 연결된 파일 이름 확장명 또는 별표(*)입니다. 이러한 매핑을 통해 모든 HTTP 동사가 응용 프로그램에 전달되지 않게 하려면 제한값을 적절하게 설정하면 됩니다. 예를 들어, Asp.dll 파일은 기본적으로 HTTP 동사 GET, HEAD 및 POST를 사용하여 요청을 처리합니다. TRACE 요청은 IIS에서 처리합니다."

라고 나와있는데 저는 * 또는 .* 를 입력하게되면

"잘못된 확장명 형식입니다." 라며 확인이 되지를 않아요

그리고 위 게시글의 중에 보면

ISAPI 에 *를 등록하면 되겠습니다.
....
추가 ISAPI 등록 법

IIS 6 의 경우입니다.

홈디렉터리 -> 구성을 클릭 -> 매핑 탭에서 와일드카드 응용 프로그램 매핑 에 삽입 클릭

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll 선택 하고 파일이 있는지 확인에 언체크

라고 쓰여져 있는데 이것은 와일드 카드 응용 프로그램 매핑(실행순서) 옆에 있는 삽입 버튼을 말씀하시는거지요??

삽입을 눌러보게되면 실행 파일 에 찾아보기를 하여

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll를 넣어주고

파일이 있는지 확인은 언체크로 끝나는 건가요?하는 건가요? 여기에는 * 를 넣을 수 있는 칸이 없어서요

ISAPI 에 *를 등록하면 되겠습니다. <<< ㅜㅜ

그리고 저는 로컬 컴퓨터에 MVC3으로 기본 적인 MVC 웹 응용 프로그램을 만들어서

바로 컴파일 하면 현재 이 웹 사이트처럼 기본적인 파란 배경에 Welcome MVC 이런 문자열을 찍게되자나요?

Home, About 있는것

이런 기본적인 웹 싸이트를 배포하는 방법에 대해 상세하게 얘기 하고 싶은뎅

가능하시다면 연락 가능한 연락처 또는 email(스크린샷 첨부를 위해서)를 알려주시면

정말 정말 감사하겠습니다..

저희가 2010-04-04 부터 제작에 들어가야하는데 배포가 불가능하다면 MVC를 쓸수 없는 상황이거든요

혼자 MVC 공부하다 너무 좋아서 이걸로 제안한 터에 배포 조차 못하고 있으니 너무 낭감합니다.

도와주세요 ㅜㅜ



유진상님의 글 (4/5/2011 11:02:27 AM)
헉 저를 아신다구요?!

설마 제가 지금 생각나는 그분인가?! +ㅁ+

저 들어온지 얼마 안있어서 쉬고 싶으시다고 전철에서 말씀하셨던?! 여성분?!

점점 도배 수준이네요... 죄송해요;; 하지만 반가운건 어쩔 수 없네요



유진상님의 글 (4/5/2011 11:12:21 AM)
제가 게시(배포)한 방법이 맞는지 확인점 부탁드릴게요

호스팅 업체에 배포를 하셨다고하셔서요

더 좋은 방법이 있는지 어떻게 하셨는지 너무너무 궁금해요

저는 우선 MVC3 웹 응용프로그램을 로컬에서 vs2010을 통해 만든뒤에

그저 프로젝트에서 우클릭 게시 기능 클릭

그럼 웹게시라는 대화상자가 나타나는데

거기서 게시 방법에 FTP를 선택
(웹 배포도 있지만 그건 IIS7, IIS7.5에 매너지먼트 서비스 딜리게이션을 통해 해야한다더라구요)
- http://weblogs.asp.net/scottgu/archive/2010/09/13/automating-deployment-with-microsoft-web-deploy.aspx

그 다음 대상 위치에 http://mtest.druginfo.co.kr를 입력해주고

일치하는 파일을 로컬 복사본으로 바꾸기에 라디오 박스 선택해주고

패시브 모드 체크 후 사용자 이름과 암호를 입력해주고 게시를 넣어주었어요

그럼

VS2010의 하단 출력창에 각 폴더 별로 "폴더를 게시하는 중...." 나타나더니 성공

그런데 자세히 보면 bin, Content, Scripts, Views 폴더만 넘어가고
그 밖의 파일은 Global.aspx, Web.config 등이 날아가는데

Controller 폴더는 전송하지 않더군요 대신 bin 폴더안에 해당 프로젝트명의 dll이 들어있기는 합니다만...

그래서 서버 주소

http://com.mycompany.co.kr
를 입력하면 기본적인 MVC 페이지가 나타나기를 기대했으나

HTTP 403 사용권한 없음 에러가 납니다 ㅜㅜ

방실 선배님은 어떻게 하셨어요?!



방실이님의 글 (4/5/2011 1:33:33 PM)
약국정보 팀이군요..
배포 서버가 5층에 있지 않나요?
따로 호스팅을 받고 있었었나..
서버에 mvc 프레임워크를 설치하면 간단하게 해결됩니다...

http://blog.stevensanderson.com/2008/07/04/options-for-deploying-aspnet-mvc-to-iis-6/

이 글을 참고 해 보세요..



유진상님의 글 (4/5/2011 2:32:27 PM)
방실 선배님 정말 정말 감사합니다.

우선 우리가 원하는 서버에 deploy를 하는 것을 성공했다고 말씀드리고 싶구요

계속 헤딩했던 이유는 무리하게 MVC3로 하려던게 문제였던거 같아요

MVC3로 하려니까 자연스럽게 Framework 4.0을 사용해야 했고 그로인해 그 외 싸이트랑

버전이 안맞아 서버를 사용할 수 없다는 에러메세지를 계속 토해내더라구요

물론 IIS안에서 각각의 웹싸이트를 버전별로 다르게 할 수 있다는건 알고 있었지만



[홈디렉터리 -> 구성을 클릭 -> 매핑 탭에서 와일드카드 응용 프로그램 매핑 에 삽입 클릭

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll 선택 하고 파일이 있는지 확인에 언체크]

이것이 문제 더라구요

물론 그당시 에는 4.0 버전의 aspnet_isapi.dll을 선택 및 파일 확인에 언체크를 착실히 했었죠

해당 4.0 버전 웹 싸이트에 위 ISAPI를 등록하게 되면(MVC 라우팅 시스템을 사용하기 위해서)

나머지 다른 정상적인 웹싸이트들까지 전부 정지가 되어버리더라구요(같은 IP로 사용하고 도메인만 다른 싸이트)

그리고 4.0 웹싸이트 에 접근하면 서버를 이용할 수 없다며 에러메세지는 ASP.NET 버전 이 서로 다르다고..

아무튼 그래서 다 지우고 3.5, 3.5sp1을 재 설치 하고 ASP.NET 2.0도 aspnet_regiis.exe -i 해주고

웹 싸이트를 2.0으로 만들고 프론트 페이지 만들어주고 간단한 htm 넣어보고 뜨는지 확인한뒤에

제 로컬에 MVC2를 만들어 게시를 했죠

게시는 ftp 방법으로 http://로 접근하지 않고 ftp주소로 접근해야 게시가 가능했어요

4.0으로 삽질할때는 http:// 해도 가능했었는데 2.0에는 서버 에러가 발생하더라구요

ftp로 하니까 게시가 가능했어요

결론은 MVC3 -> MVC2 웹 응용 프로그램

ASP.NET version 4.0 -> 2.0 으로 ...

왜 그렇게 4.0으로 하려했냐고 물으신다면...

C# 4.0을 써보고 싶어서 그랬다고 고백할게요 ㅎㅎ

아무튼 여러모로 감사합니다!!

아참 게시글에

[ISAPI 에 *를 등록하면 되겠습니다.]

이글이 많이 헷갈렸어요

*를 등록하래서 아래 삽입 버튼이 아니라 위에 추가 버튼에서 확장명을 추가해야 하는지 알고 계속 헤딩했어요 ㅜㅜ

멍청이 같이... 삽입 버튼이라구 아래 써져있는데..ㅜㅜ



방실이님의 글 (4/5/2011 7:58:57 PM)
다행이네요..^^



댓글 쓰기

MVC-Outbound Route 처리 하기

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (11/11/2010 4:37:15 PM) Viewing : 2812

이 사이트도 MVC 로 만들어 졌지만...MVC는 참으로 매력적인 아키텍쳐입니다.

조금씩 이나마 MVC 관련 글을 써 볼까 합니다. - 경험을 토대로...

오늘의 글은 라우팅 관련 글입니다.

MVC 프레임워크의 큰 장점이기도 한 Url Route 기능은 참으로 까탈 스럽습니다..ㅡ.ㅡ;;;

이 사이트의 예를 들자면 ArticleController 라는 컨트롤러가 있습니다.

이 컨트롤러는 게시판 관련 컨트롤러입니다. 이 사이트의 아주 핵심적인 컨트롤러이죠.

ArticleController에는 Index 라는 액션이 있는데 이는 기본 액션입니다.
이 기본 액션에서 사용자가 링크를 클릭 했을 경우 목록(블로그 타입이든 리스트타입이든)을 보여 주거나 하나의 게시물을 보여 주거나 하는 일을 합니다.
너무 많은 일을 하는 거죠..ㅡ.ㅡ;;;;;

어쨋든 제가 설계한 기본 Url은 다음과 같습니다.

{categoryUrlName}/{postCategoryUrlName}/{postId}

즉 이 게시물의 Url은 아래와 같겠죠.

Owner/Dotnet3x/60eba739-a622-40a2-bc52-126fb9d6cedf

만약 브라우저의 url 창에 위와 같은 url을 타이핑하고 엔터를 누르면 이 페이지가 보이게 됩니다.

이를 인바운드 라우트라고 합니다.

이 페이지가 보여지는 시점에서 각종 링크들 ActionLink와 같은 메서드를 통해 라우터를 이용한 url을 생성하는 것을 아웃바운드 라우트라고 합니다.

제 경험상..인바운드 라우팅은 쉽습니다.. url이 들어 오면 그에 맞게끔 라우터를 구성하면 되기 때문이죠..

그러나...아웃바운드 라우트 구성은 여간 까탈 스럽지 않습니다.

같은 링크 여도..예를 들면..

this.Url.Action("Index", new { categoryUrlName = category.UrlName,
 postCategoryUrlName = Utility.GetPostCategory(model.PostCategoryId).UrlName,
 postId = model.PostId })

위와 같은 액션은 현재 페이지가 어떤 컨트롤러와 액션에 위치하고 있는가에 따라 다르게 라우트됩니다.

어떠한 경우에는 {categoryUrlName}/{postCategoryUrlName}/{postId} 형식으로 정상적으로 라우트 되기도 하지만.
어떠한 경우에는 Article/Index?categoryUrlName=aaa&postCategoryUrlName=bbb&postId=ccc 처럼 라우트 되기도 합니다.

이 이유는..MVC 엔진의 캐시..기능 때문입니다..

그러나 저는 이런 상황에 따라 다르게 변하는 아웃바운드라우팅 결과를 원하지 않습니다..당연하겠죠...??

서론이 길었군요..ㅡ.ㅡ;;;;

결론 부터 말하면 사용자 정의 라우터를 만들면 됩니다. 

/// <summary>
/// Article 라우트 Url을 변조 시키기 위한 Custom Route 클래스 입니다.
/// </summary>
public class ArticleUrlsRoute : RouteBase
{
    /// <summary>
    /// When overridden in a derived class, returns route information about the request.
    /// </summary>
    /// <param name="httpContext">An object that encapsulates information about the HTTP request.</param>
    /// <returns>
    /// An object that contains the values from the route definition if the route matches the current request, 
    /// or null if the route does not match the request.
    /// </returns>
    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        return null;
    }

    /// <summary>
    /// categoryUrlName, postCategoryUrlName, postId 가 존재하면 {controller}/{action}/{id} 형식을 
    /// {categoryUrlName}/{postCategoryUrlName}/{postId} 으로 아웃바인딩 url 을 변조합니다.
    /// </summary>
    /// <param name="requestContext">An object that encapsulates information about the requested route.</param>
    /// <param name="values">An object that contains the parameters for a route.</param>
    /// <returns>
    /// An object that contains the generated URL and information about the route, 
    /// or null if the route does not match <paramref name="values"/>.
    /// </returns>
    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        if (values.Count > 0
            && values["controller"].ToString().ToLower() == "article"
            && values["action"].ToString().ToLower() == "index"
            && !String.IsNullOrEmpty((string)values["categoryUrlName"]))
        {
            values["controller"] = values["categoryUrlName"];
            values.Remove("categoryUrlName");

            if (!String.IsNullOrEmpty((string)values["postCategoryUrlName"]))
            {
                values["action"] = values["postCategoryUrlName"];
                values.Remove("postCategoryUrlName");
                
                if (values["postId"] != null)
                {
                    values["id"] = values["postId"];
                    values.Remove("postId");
                }
                else if (values["page"] != null)
                {
                    values["id"] = "Page" + values["page"];
                    values.Remove("page");
                }
            }
            return null;
        }
        else
        {
            return null;
        }
    }
}

RouteBase 클래스를 상속 받으면 위의 2개의 메서드를 재정의 해야 합니다.
GetRouteData 메서드는 위에서 설명하였던 인바운드 라우트를 관리 합니다.
그리고 GetVirtualPath 메서드는 아웃바운드 라우트를 관리합니다.

위에서 언급하였던 바와 같이 우리는 지금 아웃바운드에 관심이 있기에 GetVirtualPath 메서드를 재정의 할 것입니다.

첫번째 매개변수인 requestContext는 현재 페이지의 컨텍스트가 되겠습니다.

그리고 values는 현재 페이지에서 아웃바운드 라우트될 개체의 Route값의 사전입니다.

현재 페이지에 있는 액션 URL들은 모두 이 사전에 담겨져 전달됩니다.

즉 이 사전의 값을 조사하여 우리가 원하는 케이스가 온다면 원하는 가상경로를 변경 시켜주는 것...이 요지입니다.

개발자가 코드에서 첫번째 예시 처럼 ActionUrl을 생성 하였을 경우 아웃바운드시 위 라우터를 거치게 되면
Article/Index?categoryUrlName=aaa&postCategoryUrlName=bbb&postId=ccc 라는 URL은
{controller}/{action}/{id} 라우트 값에 매칭이 됩니다. 그래서 우리가 원하는  {categoryUrlName}/{postCategoryUrlName}/{postId} 라는 URL를 생성하게 되는 것입니다.

이제 라우트 클래스를 생성하였으니 적용만 하면 되겠지요?

global.asax 파일에 세팅하면 됩니다.

// ~/Article/Index로 들어 오는 아웃바운드 url을 변조합니다.
routes.Add(new ArticleUrlsRoute());

이거 땜시..완전 개고생 했네요...역시 알면 쉬운...^^;;


마지막 업데이트 : (11/11/2010 5:12:39 PM)

TAG : MVC Route 



Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기



<< < 1 2 3 4 5 6 7 8 9 10 > >>